<commit_before># -*- coding: utf-8 -*-
"""
This module handles the API for the system, registering backends and using them.
"""

from __future__ import absolute_import

import types
import sys
import warnings
import logging

from . import registry
from .utils.helpers import _Setting

__all__ = ['install', 'use', 'teardown', 'use_strict_xml', 'STRICT_KTL_XML', 'APISetting']

log = logging.getLogger("Cauldron.api")

CAULDRON_SETUP = _Setting("CAULDRON_SETUP", False)

class APISetting(_Setting):
    """A setting which locks with the API use() calls."""
    def __init__(self, name, value):
        super(APISetting, self).__init__(name=name, value=value, lock=CAULDRON_SETUP)

BASENAME = ".".join(__name__.split(".")[:-1])

KTL_DEFAULT_NAMES = set(['ktl', 'DFW'])

STRICT_KTL_XML = _Setting("STRICT_KTL_XML", False)

def use_strict_xml():
    """Use strict XML settings. Strict XML enforces all of the KTL XML rules to the letter, and requires that all keywords be pre-defined in XML files which are loaded when connecting a service from either the client or dispatcher side.

    If strict XML is not used, Cauldron will provide warnings when XML rules are violated, but will continue to operate."""
    STRICT_KTL_XML.on()

def use(name):
    """Activae a KTL backend in Cauldron.

    :param str name: The name of the KTL backend to use.

    You should only call this function once per interpreter session. It will raise a :exc:`RuntimeError` if it is called when Cauldron has been already set up. After this call, it is safe to make imports from Cauldron KTL API modules::

        import Cauldron
        Cauldron.use("local")
        from Cauldron import ktl

    """
    # We do some hacks here to install the module 'Cauldron.ktl' and 'Cauldron.DFW' only once this API function has been called.
    if CAULDRON_SETUP:
        raise RuntimeError("You may only call Cauldron.use() once! It is an error to activate again.")

    # Allow imports of backend modules now.
    CAULDRON_SETUP.on()

    # Set up the LROOT KTL installation differently
    if name in KTL_DEFAULT_NAMES: # pragma: no cover
        return setup_ktl_backend()

    if name not in registry.keys():
        raise ValueError("The Cauldron backend '{0}' is not registered. Available backends are {1!r}".format(
            name, list(registry.keys())))

    log.info("Cauldron initialized using backend '{0}'".format(name))
    registry.client.use(name)
    registry.dispatcher.use(name)

    Cauldron = sys.modules[BASENAME]
    # Install the client side libraries.
    from . import _ktl
    from ._ktl.Service import Service
    from ._ktl import Keyword
    _ktl.Service = Service
    _ktl.Keyword = Keyword
    Cauldron.ktl = sys.modules[BASENAME + ".ktl"] = _ktl

    # Install the dispatcher side libraries.
    from . import _DFW
    from ._DFW.Service import Service
    from ._DFW import Keyword
    _DFW.Service = Service
    _DFW.Keyword = Keyword
    Cauldron.DFW = sys.modules[BASENAME + ".DFW"] = _DFW

def setup_ktl_backend(): # pragma: no cover
    """Set up the KTL backend."""
    Cauldron = sys.modules[BASENAME]
    import ktl
    sys.modules[BASENAME + ".ktl"] = Cauldron.ktl = ktl

    import DFW
    sys.modules[BASENAME + ".DFW"] = Cauldron.DFW = DFW

def _expunge_module(module_name):
    """Given a module name, expunge it from sys.modules."""
    mod = sys.modules[module_name]
    if mod is None:
        del sys.modules[module_name]
    else:
        try:
            del sys.modules[module_name]
        except: # pragma: no cover
            pass
    del mod

def _is_target_module(name, module_name):
    """Check a module name."""
    return (name in module_name.split(".") or "_{0}".format(name) in module_name.split(".")) and module_name.startswith(BASENAME)

def teardown():
    """Remove the Cauldron setup from the sys.modules cache, and prepare for another call to :func:`use`.

    This method can be used to reset the state of the Cauldron module and backend. This is most appropriate in a test environemnt.

    .. warning::
        It is not guaranteed to replace modules which are currently imported and active. In fact, it suffers from many
        of the same problems faced by the builtin :func:`reload`, and to a greater extent, makes very little effort to
        ensure that python objects which have already been created and belong to the Cauldron API are handled correctly.
        It is likely that if you call this method with instances of Keyword or Service still active in your application,
        those instances will become unusable.
    """
    if registry.client.backend is not None:
        name = registry.client.backend
    else:
        name = "unknown"
    registry.teardown()
    try:
        Cauldron = sys.modules[BASENAME]
        if hasattr(Cauldron, 'DFW'):
            del Cauldron.DFW
        for module_name in sys.modules.keys():
            if _is_target_module("DFW", module_name):
                _expunge_module(module_name)
        if hasattr(Cauldron, 'ktl'):
            del Cauldron.ktl
        for module_name in sys.modules.keys():
            if _is_target_module("ktl", module_name):
                _expunge_module(module_name)
        if "ktl" in sys.modules:
            del sys.modules['ktl']
        if "DFW" in sys.modules:
            del sys.modules["DFW"]
    except: # pragma: no cover
        raise
    finally:
        CAULDRON_SETUP.off()
        STRICT_KTL_XML.off()
        log.info("Cauldron teardown from backend '{0}'".format(name))

def install():
    """Install the Cauldron modules in the global namespace, so that they will intercept root-level imports.

    This method performs a runtime hack to try to make the Cauldron versions of ``ktl`` and ``DFW`` the ones which are
    accessed when a python module performs ``import ktl`` or ``import DFW``. If either module has already been imported
    in python, then this function will send a RuntimeWarning to that effect and do nothing.

    .. note:: It is preferable to use :ref:`cauldron-style`, of the form ``from Cauldron import ktl``, as this will properly ensure that the Cauldron backend is invoked and not the KTL backend.
    """
    guard_use("installing Cauldron", error=RuntimeError)
    if 'ktl' in sys.modules or "DFW" in sys.modules: # pragma: no cover
        warnings.warn("'ktl' or 'DFW' already in sys.modules. Skipping 'install()'")
        return
    sys.modules['ktl'] = sys.modules[BASENAME + ".ktl"]
    sys.modules['DFW'] = sys.modules[BASENAME + ".DFW"]

def guard_use(msg='doing this', error=RuntimeError):
    """Guard against using a Cauldron module when we haven't yet specified the backend."""
    if not CAULDRON_SETUP:
        raise error("You must call Cauldron.use() before {0} in order to set the Cauldron backend.".format(msg))

@registry.client.setup_for('all')
def setup_client_service_module():
    """Set up the client Service module."""
    from ._ktl import Service
    Service.Service = registry.client.Service

@registry.client.teardown_for("all")
def teardown_client_service_module():
    """Remove the service from the client module."""
    try:
        _ktls = sys.modules[BASENAME + "._ktl.Service"]
        del _ktls.Service
        _ktl = sys.modules[BASENAME + "._ktl"]
        del _ktl.Service
        del _ktl.Keyword
    except KeyError as e:
        pass

@registry.dispatcher.teardown_for("all")
def teardown_dispatcher_service_module():
    """Remove the service from the dispatcher module."""
    try:
        _DFWs = sys.modules[BASENAME + "._DFW.Service"]
        del _DFWs.Service
        _DFW = sys.modules[BASENAME + "._DFW"]
        del _DFW.Service
        del _DFW.Keyword
    except KeyError as e:
        pass

@registry.dispatcher.setup_for('all')
def setup_dispatcher_service_module():
    """Set up the dispatcher Service module."""
    from ._DFW import Service
    Service.Service = registry.dispatcher.Service

<commit_msg>Fixing some subtleties in module reloading.<commit_after># -*- coding: utf-8 -*-
"""
This module handles the API for the system, registering backends and using them.
"""

from __future__ import absolute_import

import types
import sys
import warnings
import logging

from . import registry
from .utils.helpers import _Setting

__all__ = ['install', 'use', 'teardown', 'use_strict_xml', 'STRICT_KTL_XML', 'APISetting']

log = logging.getLogger("Cauldron.api")

CAULDRON_SETUP = _Setting("CAULDRON_SETUP", False)

class APISetting(_Setting):
    """A setting which locks with the API use() calls."""
    def __init__(self, name, value):
        super(APISetting, self).__init__(name=name, value=value, lock=CAULDRON_SETUP)

BASENAME = ".".join(__name__.split(".")[:-1])

KTL_DEFAULT_NAMES = set(['ktl', 'DFW'])

STRICT_KTL_XML = _Setting("STRICT_KTL_XML", False)

def use_strict_xml():
    """Use strict XML settings. Strict XML enforces all of the KTL XML rules to the letter, and requires that all keywords be pre-defined in XML files which are loaded when connecting a service from either the client or dispatcher side.

    If strict XML is not used, Cauldron will provide warnings when XML rules are violated, but will continue to operate."""
    STRICT_KTL_XML.on()

def use(name):
    """Activae a KTL backend in Cauldron.

    :param str name: The name of the KTL backend to use.

    You should only call this function once per interpreter session. It will raise a :exc:`RuntimeError` if it is called when Cauldron has been already set up. After this call, it is safe to make imports from Cauldron KTL API modules::

        import Cauldron
        Cauldron.use("local")
        from Cauldron import ktl

    """
    # We do some hacks here to install the module 'Cauldron.ktl' and 'Cauldron.DFW' only once this API function has been called.
    if CAULDRON_SETUP:
        raise RuntimeError("You may only call Cauldron.use() once! It is an error to activate again.")

    # Allow imports of backend modules now.
    CAULDRON_SETUP.on()

    # Set up the LROOT KTL installation differently
    if name in KTL_DEFAULT_NAMES: # pragma: no cover
        return setup_ktl_backend()

    if name not in registry.keys():
        raise ValueError("The Cauldron backend '{0}' is not registered. Available backends are {1!r}".format(
            name, list(registry.keys())))

    log.info("Cauldron initialized using backend '{0}'".format(name))
    registry.client.use(name)
    registry.dispatcher.use(name)

    Cauldron = sys.modules[BASENAME]
    # Install the client side libraries.
    from Cauldron import _ktl
    sys.modules.setdefault('Cauldron._ktl', _ktl)
    reload(_ktl)

    from Cauldron._ktl.Service import Service
    from Cauldron._ktl import Keyword
    _ktl.Service = Service
    _ktl.Keyword = Keyword
    Cauldron.ktl = sys.modules[BASENAME + ".ktl"] = _ktl

    # Install the dispatcher side libraries.
    from Cauldron import _DFW
    sys.modules.setdefault('Cauldron._DFW', _DFW)
    reload(_DFW)
    from Cauldron._DFW.Service import Service
    from Cauldron._DFW import Keyword
    _DFW.Service = Service
    _DFW.Keyword = Keyword
    Cauldron.DFW = sys.modules[BASENAME + ".DFW"] = _DFW

def setup_ktl_backend(): # pragma: no cover
    """Set up the KTL backend."""
    Cauldron = sys.modules[BASENAME]
    import ktl
    sys.modules[BASENAME + ".ktl"] = Cauldron.ktl = ktl

    import DFW
    sys.modules[BASENAME + ".DFW"] = Cauldron.DFW = DFW

def _expunge_module(module_name):
    """Given a module name, expunge it from sys.modules."""
    mod = sys.modules[module_name]
    if mod is None:
        del sys.modules[module_name]
    else:
        try:
            del sys.modules[module_name]
        except: # pragma: no cover
            pass
    del mod

def _is_target_module(name, module_name):
    """Check a module name."""
    return (name in module_name.split(".") or "_{0}".format(name) in module_name.split(".")) and module_name.startswith(BASENAME)

def teardown():
    """Remove the Cauldron setup from the sys.modules cache, and prepare for another call to :func:`use`.

    This method can be used to reset the state of the Cauldron module and backend. This is most appropriate in a test environemnt.

    .. warning::
        It is not guaranteed to replace modules which are currently imported and active. In fact, it suffers from many
        of the same problems faced by the builtin :func:`reload`, and to a greater extent, makes very little effort to
        ensure that python objects which have already been created and belong to the Cauldron API are handled correctly.
        It is likely that if you call this method with instances of Keyword or Service still active in your application,
        those instances will become unusable.
    """
    if registry.client.backend is not None:
        name = registry.client.backend
    else:
        name = "unknown"
    registry.teardown()
    try:
        Cauldron = sys.modules[BASENAME]
        if hasattr(Cauldron, 'DFW'):
            del Cauldron.DFW
        for module_name in sys.modules.keys():
            if _is_target_module("DFW", module_name):
                _expunge_module(module_name)
        if hasattr(Cauldron, 'ktl'):
            del Cauldron.ktl
        for module_name in sys.modules.keys():
            if _is_target_module("ktl", module_name):
                _expunge_module(module_name)
        if "ktl" in sys.modules:
            del sys.modules['ktl']
        if "DFW" in sys.modules:
            del sys.modules["DFW"]
    except: # pragma: no cover
        raise
    finally:
        CAULDRON_SETUP.off()
        STRICT_KTL_XML.off()
        log.info("Cauldron teardown from backend '{0}'".format(name))

def install():
    """Install the Cauldron modules in the global namespace, so that they will intercept root-level imports.

    This method performs a runtime hack to try to make the Cauldron versions of ``ktl`` and ``DFW`` the ones which are
    accessed when a python module performs ``import ktl`` or ``import DFW``. If either module has already been imported
    in python, then this function will send a RuntimeWarning to that effect and do nothing.

    .. note:: It is preferable to use :ref:`cauldron-style`, of the form ``from Cauldron import ktl``, as this will properly ensure that the Cauldron backend is invoked and not the KTL backend.
    """
    guard_use("installing Cauldron", error=RuntimeError)
    if 'ktl' in sys.modules or "DFW" in sys.modules: # pragma: no cover
        warnings.warn("'ktl' or 'DFW' already in sys.modules. Skipping 'install()'")
        return
    sys.modules['ktl'] = sys.modules[BASENAME + ".ktl"]
    sys.modules['DFW'] = sys.modules[BASENAME + ".DFW"]

def guard_use(msg='doing this', error=RuntimeError):
    """Guard against using a Cauldron module when we haven't yet specified the backend."""
    if not CAULDRON_SETUP:
        raise error("You must call Cauldron.use() before {0} in order to set the Cauldron backend.".format(msg))

@registry.client.setup_for('all')
def setup_client_service_module():
    """Set up the client Service module."""
    from ._ktl import Service
    Service.Service = registry.client.Service

@registry.client.teardown_for("all")
def teardown_client_service_module():
    """Remove the service from the client module."""
    try:
        _ktls = sys.modules[BASENAME + "._ktl.Service"]
        del _ktls.Service
        _ktl = sys.modules[BASENAME + "._ktl"]
        del _ktl.Service
        del _ktl.Keyword
    except KeyError as e:
        pass

@registry.dispatcher.teardown_for("all")
def teardown_dispatcher_service_module():
    """Remove the service from the dispatcher module."""
    try:
        _DFWs = sys.modules[BASENAME + "._DFW.Service"]
        del _DFWs.Service
        _DFW = sys.modules[BASENAME + "._DFW"]
        del _DFW.Service
        del _DFW.Keyword
    except KeyError as e:
        pass

@registry.dispatcher.setup_for('all')
def setup_dispatcher_service_module():
    """Set up the dispatcher Service module."""
    from ._DFW import Service
    Service.Service = registry.dispatcher.Service

<|endoftext|>


<commit_before>#  Demonstration code developed bu Mayo Clinic Radiology Informatics Laboratory
# Copyright 2016
#
import os
#
# Module 1 - Data Load / Display / Normalization
#
# we will directly read DICOM files. We will use a library called pyDicom
# if you get an error on this statement, you need to install the library:
# pip install pydicom
import dicom as pydicom

# same for these libraries
import numpy as np
import matplotlib.pyplot as plt
import pylab
# from matplotlib import cm

CurrentDir, CurrentFile = os.path.split(__file__)

# Here are the file names--these files should be in the same directory as this file
PreName =  os.path.abspath(os.path.join(os.path.dirname( __file__ ), os.pardir, "Data",'Pre.dcm') )
PostName =  os.path.abspath(os.path.join(os.path.dirname( __file__ ), os.pardir, "Data",  'Post.dcm')  )
T2Name =  os.path.abspath(os.path.join(os.path.dirname( __file__ ), os.pardir, "Data", 'T2.dcm') )
FLAIRName =  os.path.abspath(os.path.join(os.path.dirname( __file__ ), os.pardir, "Data", 'FLAIR.dcm') )

# read Pre in--we assume that all images are same x,y dims
Pre = pydicom.read_file(PreName)
xdim = (int) (Pre.Rows)
ydim = (int) (Pre.Columns)

# make space in a numpy array for the images
ArrayDicom = np.zeros((xdim, ydim, 4), dtype=Pre.pixel_array.dtype)

# copy Pre pixels into z=0
ArrayDicom[:, :, 0] = Pre.pixel_array
# Post
Post = pydicom.read_file(PostName)
ArrayDicom[:, :, 1] = Post.pixel_array
# T2
T2 = pydicom.read_file(T2Name)
ArrayDicom[:, :, 2] = T2.pixel_array
#FLAIR
FLAIR = pydicom.read_file(FLAIRName)
ArrayDicom[:, :, 3] = FLAIR.pixel_array
print ("Data Loaded")

# note that the 4 brain slices are all aligned
# the rectangles here are known to represent the tissues of interest:
# Tissue0 = NAWM, Tissue 1 = GM, Tissue2 = CSF, Tissue3 = Air
NAWM = 0
GM = 1
CSF = 2
AIR = 3
TUMOR = 4
# each tissue has x1, y1, x2, y2 as rectangle in image that represents that tissue
Tissues = np.zeros((5, 2, 2), dtype=np.int32)
Tissues[NAWM][0][0] = 145 # we measured the location of these tissues
Tissues[NAWM][0][1] = 68
Tissues[NAWM][1][0] = 157
Tissues[NAWM][1][1] = 74

Tissues[GM][0][0] = 166 # we measured the location of these tissues
Tissues[GM][0][1] = 58
Tissues[GM][1][0] = 175
Tissues[GM][1][1] = 62

Tissues[CSF][0][0] = 105 # we measured the location of these tissues
Tissues[CSF][0][1] = 148
Tissues[CSF][1][0] = 120
Tissues[CSF][1][1] = 154

Tissues[AIR][0][0] = 1 # we measured the location of these tissues
Tissues[AIR][0][1] = 1
Tissues[AIR][1][0] = 10
Tissues[AIR][1][1] = 10

Tissues[TUMOR][0][0] = 65 # we measured the location of these tissues
Tissues[TUMOR][0][1] = 130
Tissues[TUMOR][1][0] = 74
Tissues[TUMOR][1][1] = 139

plt.figure()
plt.imshow(np.flipud(Pre.pixel_array),cmap='gray')
plt.contour(Tissues[NAWM], colors='k', origin='image')
plt.axis([0,256,0,256])
plt.show()

<commit_msg>Add scatter plot and basic classifier comparison, svm example for diffrent kernels as well as an example to understand support vectors<commit_after>#  Demonstration code developed bu Mayo Clinic Radiology Informatics Laboratory
# Copyright 2016
#
import os
#
# Module 1 - Data Load / Display / Normalization
#
# we will directly read DICOM files. We will use a library called pyDicom
# if you get an error on this statement, you need to install the library:
# pip install pydicom
import dicom as pydicom
# same for these libraries
import numpy as np
import matplotlib.pyplot as plt
import pylab
from mpl_toolkits.mplot3d import Axes3D
from sklearn import svm

from matplotlib.colors import ListedColormap
from sklearn.cross_validation import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_moons, make_circles, make_classification
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis
# from matplotlib import cm

CurrentDir, CurrentFile = os.path.split(__file__)

# Here are the file names--these files should be in the same directory as this file
PreName =  os.path.abspath(os.path.join(os.path.dirname( __file__ ), os.pardir, "Data",'Pre.dcm') )
PostName =  os.path.abspath(os.path.join(os.path.dirname( __file__ ), os.pardir, "Data",  'Post.dcm')  )
T2Name =  os.path.abspath(os.path.join(os.path.dirname( __file__ ), os.pardir, "Data", 'T2.dcm') )
FLAIRName =  os.path.abspath(os.path.join(os.path.dirname( __file__ ), os.pardir, "Data", 'FLAIR.dcm') )

# read Pre in--we assume that all images are same x,y dims
Pre = pydicom.read_file(PreName)
xdim = (int) (Pre.Rows)
ydim = (int) (Pre.Columns)

# make space in a numpy array for the images
ArrayDicom = np.zeros((xdim, ydim, 4), dtype=Pre.pixel_array.dtype)

# copy Pre pixels into z=0
ArrayDicom[:, :, 0] = Pre.pixel_array
# Post
Post = pydicom.read_file(PostName)
ArrayDicom[:, :, 1] = Post.pixel_array
# T2
T2 = pydicom.read_file(T2Name)
ArrayDicom[:, :, 2] = T2.pixel_array
#FLAIR
FLAIR = pydicom.read_file(FLAIRName)
ArrayDicom[:, :, 3] = FLAIR.pixel_array
print ("Data Loaded")

# note that the 4 brain slices are all aligned
# the rectangles here are known to represent the tissues of interest:
# Tissue0 = NAWM, Tissue 1 = GM, Tissue2 = CSF, Tissue3 = Air
NAWM = 0
GM = 1
CSF = 2
AIR = 3
TUMOR = 4
# each tissue has x1, y1, x2, y2 as rectangle in image that represents that tissue
Tissues = np.zeros((5, 2, 2), dtype=np.int32)
Tissues[NAWM][0][0] = 145 # we measured the location of these tissues
Tissues[NAWM][0][1] = 68
Tissues[NAWM][1][0] = 157
Tissues[NAWM][1][1] = 74

Tissues[GM][0][0] = 166 # we measured the location of these tissues
Tissues[GM][0][1] = 58
Tissues[GM][1][0] = 175
Tissues[GM][1][1] = 62

Tissues[CSF][0][0] = 105 # we measured the location of these tissues
Tissues[CSF][0][1] = 148
Tissues[CSF][1][0] = 120
Tissues[CSF][1][1] = 154

Tissues[AIR][0][0] = 1 # we measured the location of these tissues
Tissues[AIR][0][1] = 1
Tissues[AIR][1][0] = 10
Tissues[AIR][1][1] = 10

Tissues[TUMOR][0][0] = 65 # we measured the location of these tissues
Tissues[TUMOR][0][1] = 130
Tissues[TUMOR][1][0] = 74
Tissues[TUMOR][1][1] = 139

plt.figure()
plt.imshow(np.flipud(Pre.pixel_array),cmap='gray')
plt.contour(Tissues[NAWM], colors='k', origin='image')
plt.axis([0,256,0,256])
plt.show()

# Create classes
ClassNAWMpost=ArrayDicom[Tissues[NAWM][0][0]:Tissues[NAWM][1][0],Tissues[NAWM][0][1]:Tissues[NAWM][1][1],0]
ClassNAWMpret=ArrayDicom[Tissues[NAWM][0][0]:Tissues[NAWM][1][0],Tissues[NAWM][0][1]:Tissues[NAWM][1][1],1]
ClassNAWMT2=ArrayDicom[Tissues[NAWM][0][0]:Tissues[NAWM][1][0],Tissues[NAWM][0][1]:Tissues[NAWM][1][1],2]
ClassNAWMFLAIR=ArrayDicom[Tissues[NAWM][0][0]:Tissues[NAWM][1][0],Tissues[NAWM][0][1]:Tissues[NAWM][1][1],3]
ClassTUMORpost=ArrayDicom[Tissues[TUMOR][0][0]:Tissues[TUMOR][1][0],Tissues[TUMOR][0][1]:Tissues[TUMOR][1][1],0]
ClassTUMORpret=ArrayDicom[Tissues[TUMOR][0][0]:Tissues[TUMOR][1][0],Tissues[TUMOR][0][1]:Tissues[TUMOR][1][1],1]
ClassTUMORT2=ArrayDicom[Tissues[TUMOR][0][0]:Tissues[TUMOR][1][0],Tissues[TUMOR][0][1]:Tissues[TUMOR][1][1],2]
ClassTUMORFLAIR=ArrayDicom[Tissues[TUMOR][0][0]:Tissues[TUMOR][1][0],Tissues[TUMOR][0][1]:Tissues[TUMOR][1][1],3]

# Display Tumor vs NAWM
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(ClassNAWMpost.reshape(-1), ClassNAWMpret.reshape(-1), ClassNAWMFLAIR.reshape(-1))
ax.scatter(ClassTUMORpost.reshape(-1), ClassTUMORpret.reshape(-1), ClassTUMORFLAIR.reshape(-1), c='r', marker='^')
ax.set_xlabel('post')
ax.set_ylabel('pret')
ax.set_zlabel('FLAIR')
plt.show()

# run svm classifiers

X_1 = np.stack((ClassNAWMpost.reshape(-1),ClassNAWMpret.reshape(-1)), axis=-1) # we only take the first two features.
X_2 = np.stack((ClassTUMORpost.reshape(-1),ClassTUMORpret.reshape(-1)),axis=-1)
print (X_1)
# print (np.shape(X_1))
print (X_2)
# print (np.shape(X_2))
X=np.vstack((X_1,X_2))
y =np.zeros((np.shape(X))[0])
y[np.shape(X_1)[0]:]=1
h = .02  # step size in the mesh

# we create an instance of SVM and fit out data. We do not scale our
# data since we want to plot the support vectors
C = 1.0  # SVM regularization parameter
svc = svm.SVC(kernel='linear', C=C).fit(X, y)
rbf_svc = svm.SVC(kernel='rbf', gamma=0.1, C=10).fit(X, y)
poly_svc = svm.SVC(kernel='poly', degree=3, C=C).fit(X, y)
lin_svc = svm.LinearSVC(C=C).fit(X, y)

# create a mesh to plot in
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                     np.arange(y_min, y_max, h))

# title for the plots
titles = ['SVC with linear kernel',
          'LinearSVC (linear kernel)',
          'SVC with RBF kernel',
          'SVC with polynomial (degree 3) kernel']

for i, clf in enumerate((svc, lin_svc, rbf_svc, poly_svc)):
    # Plot the decision boundary. For that, we will assign a color to each
    # point in the mesh [x_min, m_max]x[y_min, y_max].
    plt.subplot(2, 2, i + 1)
    plt.subplots_adjust(wspace=0.4, hspace=0.4)

    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

    # Put the result into a color plot
    Z = Z.reshape(xx.shape)
    plt.contourf(xx, yy, Z, cmap=plt.cm.Paired, alpha=0.8)

    # Plot also the training points
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired)
    plt.xlabel('Intensity post contrast')
    plt.ylabel('Intensity pre contrast')
    plt.xlim(xx.min(), xx.max())
    plt.ylim(yy.min(), yy.max())
    plt.xticks(())
    plt.yticks(())
    plt.title(titles[i])

plt.show()

# understanding margins

clf = svm.SVC(kernel='linear')
clf.fit(X, y)

# get the separating hyperplane
w = clf.coef_[0]
a = -w[0] / w[1]
xx = np.linspace(-5, 5)
yy = a * xx - (clf.intercept_[0]) / w[1]

# plot the parallels to the separating hyperplane that pass through the
# support vectors
b = clf.support_vectors_[0]
yy_down = a * xx + (b[1] - a * b[0])
b = clf.support_vectors_[-1]
yy_up = a * xx + (b[1] - a * b[0])

# plot the line, the points, and the nearest vectors to the plane
plt.plot(xx, yy, 'k-')
plt.plot(xx, yy_down, 'k--')
plt.plot(xx, yy_up, 'k--')

plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1],
            s=80, facecolors='none')
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired)

plt.axis('tight')
plt.show()

# compare all classifiers

h = .02  # step size in the mesh
linearly_separable = (X, y)
names = ["Nearest Neighbors", "Linear SVM", "RBF SVM", "Decision Tree",
         "Random Forest", "AdaBoost", "Naive Bayes", "Linear Discriminant Analysis",
         "Quadratic Discriminant Analysis"]
classifiers = [
    KNeighborsClassifier(3),
    SVC(kernel="linear", C=0.025),
    SVC(gamma=2, C=1),
    DecisionTreeClassifier(max_depth=5),
    RandomForestClassifier(max_depth=5, n_estimators=10, max_features=1),
    AdaBoostClassifier(),
    GaussianNB(),
    LinearDiscriminantAnalysis(),
    QuadraticDiscriminantAnalysis()]

datasets = [linearly_separable
            ]

figure = plt.figure(figsize=(27, 9))
i = 1
# iterate over datasets
for ds in datasets:
    # preprocess dataset, split into training and test part
    X, y = ds
    X = StandardScaler().fit_transform(X)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.4)

    x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
    y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))

    # just plot the dataset first
    cm = plt.cm.RdBu
    cm_bright = ListedColormap(['#FF0000', '#0000FF'])
    ax = plt.subplot(len(datasets), len(classifiers) + 1, i)
    # Plot the training points
    ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright)
    # and testing points
    ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=cm_bright, alpha=0.6)
    ax.set_xlim(xx.min(), xx.max())
    ax.set_ylim(yy.min(), yy.max())
    ax.set_xticks(())
    ax.set_yticks(())
    i += 1

    # iterate over classifiers
    for name, clf in zip(names, classifiers):
        ax = plt.subplot(len(datasets), len(classifiers) + 1, i)
        clf.fit(X_train, y_train)
        score = clf.score(X_test, y_test)

        # Plot the decision boundary. For that, we will assign a color to each
        # point in the mesh [x_min, m_max]x[y_min, y_max].
        if hasattr(clf, "decision_function"):
            Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
        else:
            Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]

        # Put the result into a color plot
        Z = Z.reshape(xx.shape)
        ax.contourf(xx, yy, Z, cmap=cm, alpha=.8)

        # Plot also the training points
        ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright)
        # and testing points
        ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=cm_bright,
                   alpha=0.6)

        ax.set_xlim(xx.min(), xx.max())
        ax.set_ylim(yy.min(), yy.max())
        ax.set_xticks(())
        ax.set_yticks(())
        ax.set_title(name)
        ax.text(xx.max() - .3, yy.min() + .3, ('%.2f' % score).lstrip('0'),
                size=15, horizontalalignment='right')
        i += 1

figure.subplots_adjust(left=.02, right=.98)
plt.show()
<|endoftext|>


<commit_before>from time import process_time

import numpy as np
from scipy.stats import multivariate_normal as mnorm

def em_gmm(data, init_mu, init_sigma, init_mix, num_iter=100, verbose=False):
    '''
    Estimate Gaussian mixture models with EM algorithm.

    Parameters
    ----------
    data : 2-d array
        Data to cluster.
    init_mu : 2-d array
        Array of initial means. init_mu[k] should provide the mean vector
        for the kth group.
    init_sigma : list of 2-d arrays or 3-d array
        Initial covariance matrices. init_sigma[k] should provide the
        covariance matrix for the kth group.
    init_mix : array
        Initial mixing components.
    num_iter : int, optional
        Number of EM iterations to run. (default 100)
    verbose : bool, optional
        Set to true to print status updates.

    Returns
    -------
    curr_mu : 2-d array
    curr_sigma : list of 2-d arrays or 3-d array
    curr_mix : array
        Final estimates of the mean, covariance, and mixture components.

    '''
    if len(init_mu) != len(init_sigma) or len(init_sigma) != len(init_mix):
        raise ValueError(
            'Number of initial values needs to be consistent.')
    if not np.isclose(sum(init_mix), 1):
        raise ValueError(
            'Initial mixing components should add to 1.')

    num_groups = len(init_mu)
    n = len(data)
    try:
        p = len(init_mu[0])
    except TypeError:
        p = 1

    curr_mu = init_mu
    curr_sigma = init_sigma
    curr_mix = init_mix
    logliks = np.zeros(num_iter+1)
    times = np.zeros(num_iter+1)

    start_time = process_time()
    for iternum in range(num_iter):
        if verbose:  # Status updates
            if np.isclose(iternum // 100, iternum / 100) and iternum != 0:
                print()
            if np.isclose(iternum // 10, iternum / 10) and iternum != 0:
                print('.', end='', flush=True)

        # E-step
        pdfs = calc_pdfs(data, curr_mu, curr_sigma)
        probs = _calc_probs(pdfs, curr_mix)

        logliks[iternum] = calc_loglik(data, pdfs, probs)

        # M-step
        for k in range(num_groups):
            curr_mu[k] = np.average(data, axis=0, weights=probs[:,k])
            curr_sigma[k] = np.cov(data, rowvar=0, aweights=probs[:,k], ddof=0)
        curr_mix = np.mean(probs, axis=0)

        times[iternum+1] = process_time() - start_time

    logliks[-1] = calc_loglik(
        data,
        calc_pdfs(data, curr_mu, curr_sigma),
        _calc_probs(pdfs, curr_mix)
    )

    return (curr_mu, curr_sigma, curr_mix), (logliks, times)

def _calc_probs(pdfs, mix):
    weighted_pdfs = mix*pdfs
    tot_pdfs = np.sum(weighted_pdfs, axis=1)
    probs = weighted_pdfs / tot_pdfs[:,np.newaxis]
    return probs

def calc_pdfs(data, mus, sigmas):
    '''
    Calculates pdf of each observation for each group.

    Parameters
    ----------
    data : 2-d array
    mus : 2-d array
        Array of mean vectors.
    sigmas : list of 2-d arrays or 3-d array
        Covariance matrices.

    Returns
    -------
    pdfs : 2-d array
        pdfs[i, j] contains the pdf for the ith observation using the jth
        group.

    '''
    num_groups = len(mus)
    pdfs = np.zeros((len(data), num_groups))
    for k in range(num_groups):
        pdfs[:,k] = mnorm.pdf(data, mus[k], sigmas[k])
    return pdfs

def calc_loglik(data, pdfs, probs):
    '''
    Calculates the log likelihood of the data. If probs is an indicator, then
    the log likelihood of the data is
    ``log p(x) = sum_i sum_k probs_ik log p_k(x_i)''
    This motivates the weighted log-likelihood to use when probs contains
    probabilities which has the same formula.

    Parameters
    ----------
    data : 2-d array
    pdfs : 2-d array
        Array of pdfs of each observation for each group.
    probs : 2-d array
        Array of the probability that each observation is in each group.

    Returns
    -------
    loglik : float
        Log likelihood of the data.

    '''
    logpdfs = np.log(pdfs)
    loglik = np.sum(probs * logpdfs)
    return(loglik)

if __name__ == '__main__':
    import matplotlib.pyplot as plt
    from matplotlib import colors

    n = 2000
    np.random.seed(29643)

    # Simulate 3 groups
    num_groups = 3
    mu = np.array([[0., 2.], [2., 1.], [2., 3.]])
    sigma = np.array(
        [[[1.0,  0.1], [ 0.1, 0.3]],
         [[1.0, -0.1], [-0.1, 1.0]],
         [[0.5, -0.5], [-0.5, 1.0]]])
    mix = np.array([.15, .7, .15])

    xs = [mnorm.rvs(mu[k], sigma[k], size=n) for k in range(3)]
    z = np.random.multinomial(1, mix, size=n).astype('Bool')

    x = xs[0].copy()
    x[z[:,1]] = xs[1][z[:,1]]
    x[z[:,2]] = xs[2][z[:,2]]

    z_ind = np.zeros(n)
    z_ind[z[:,1]] = 1
    z_ind[z[:,2]] = 2

    # Plot data
    cmap, norm = colors.from_levels_and_colors(
        levels=[0, 1, 2], colors=['magenta', 'cyan', 'green'], extend='max')

    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.scatter(x[:,0], x[:,1], c=z_ind, cmap=cmap, norm=norm)
    fig.savefig('./sim_data.pdf')

    # Estimate
    init_mu = np.array([[0., 0.], [1., 1.], [2., 2.]])
    init_sigma = [np.identity(2) for i in range(3)]
    init_mix = np.array([1., 1., 1.])/3

    res, (logliks, times) = em_gmm(
        x, init_mu, init_sigma, init_mix, num_iter=400)

    # Plotting
    data_mu = np.array(
        [np.mean(x[z_ind == k], axis=0) for k in range(num_groups)])
    data_sigma = np.array(
        [np.cov(x[z_ind == k], rowvar=0) for k in range(num_groups)])

    data_loglik = calc_loglik(
        x, calc_pdfs(x, data_mu, data_sigma), z)
    true_loglik = calc_loglik(x, calc_pdfs(x, mu, sigma), z)

    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(logliks)
    ax.axhline(y=data_loglik, color='green')
    ax.axhline(y=true_loglik, color='red')
    fig.savefig('./logliks.pdf')
<commit_msg>Adjusted timing to ignore likelihood calculation.<commit_after>from time import process_time

import numpy as np
from scipy.stats import multivariate_normal as mnorm

def em_gmm(data, init_mu, init_sigma, init_mix, num_iter=100, verbose=False):
    '''
    Estimate Gaussian mixture models with EM algorithm.

    Parameters
    ----------
    data : 2-d array
        Data to cluster.
    init_mu : 2-d array
        Array of initial means. init_mu[k] should provide the mean vector
        for the kth group.
    init_sigma : list of 2-d arrays or 3-d array
        Initial covariance matrices. init_sigma[k] should provide the
        covariance matrix for the kth group.
    init_mix : array
        Initial mixing components.
    num_iter : int, optional
        Number of EM iterations to run. (default 100)
    verbose : bool, optional
        Set to true to print status updates.

    Returns
    -------
    curr_mu : 2-d array
    curr_sigma : list of 2-d arrays or 3-d array
    curr_mix : array
        Final estimates of the mean, covariance, and mixture components.

    '''
    if len(init_mu) != len(init_sigma) or len(init_sigma) != len(init_mix):
        raise ValueError(
            'Number of initial values needs to be consistent.')
    if not np.isclose(sum(init_mix), 1):
        raise ValueError(
            'Initial mixing components should add to 1.')

    num_groups = len(init_mu)
    n = len(data)
    try:
        p = len(init_mu[0])
    except TypeError:
        p = 1

    curr_mu = init_mu
    curr_sigma = init_sigma
    curr_mix = init_mix
    logliks = np.zeros(num_iter+1)
    time_iter = np.zeros(num_iter+1)

    start_time = process_time()
    for iternum in range(num_iter):
        if verbose:  # Status updates
            if np.isclose(iternum // 100, iternum / 100) and iternum != 0:
                print()
            if np.isclose(iternum // 10, iternum / 10) and iternum != 0:
                print('.', end='', flush=True)

        # E-step
        start = process_time()
        pdfs = calc_pdfs(data, curr_mu, curr_sigma)
        probs = _calc_probs(pdfs, curr_mix)
        time_iter[iternum+1] += process_time() - start

        logliks[iternum] = calc_loglik(data, pdfs, probs)

        # M-step
        start = process_time()
        for k in range(num_groups):
            curr_mu[k] = np.average(data, axis=0, weights=probs[:,k])
            curr_sigma[k] = np.cov(data, rowvar=0, aweights=probs[:,k], ddof=0)
        curr_mix = np.mean(probs, axis=0)
        time_iter[iternum+1] += process_time() - start

    logliks[-1] = calc_loglik(
        data,
        calc_pdfs(data, curr_mu, curr_sigma),
        _calc_probs(pdfs, curr_mix)
    )
    times = np.cumsum(time_iter)

    return (curr_mu, curr_sigma, curr_mix), (logliks, times)

def _calc_probs(pdfs, mix):
    weighted_pdfs = mix*pdfs
    tot_pdfs = np.sum(weighted_pdfs, axis=1)
    probs = weighted_pdfs / tot_pdfs[:,np.newaxis]
    return probs

def calc_pdfs(data, mus, sigmas):
    '''
    Calculates pdf of each observation for each group.

    Parameters
    ----------
    data : 2-d array
    mus : 2-d array
        Array of mean vectors.
    sigmas : list of 2-d arrays or 3-d array
        Covariance matrices.

    Returns
    -------
    pdfs : 2-d array
        pdfs[i, j] contains the pdf for the ith observation using the jth
        group.

    '''
    num_groups = len(mus)
    pdfs = np.zeros((len(data), num_groups))
    for k in range(num_groups):
        pdfs[:,k] = mnorm.pdf(data, mus[k], sigmas[k])
    return pdfs

def calc_loglik(data, pdfs, probs):
    '''
    Calculates the log likelihood of the data. If probs is an indicator, then
    the log likelihood of the data is
    ``log p(x) = sum_i sum_k probs_ik log p_k(x_i)''
    This motivates the weighted log-likelihood to use when probs contains
    probabilities which has the same formula.

    Parameters
    ----------
    data : 2-d array
    pdfs : 2-d array
        Array of pdfs of each observation for each group.
    probs : 2-d array
        Array of the probability that each observation is in each group.

    Returns
    -------
    loglik : float
        Log likelihood of the data.

    '''
    logpdfs = np.log(pdfs)
    loglik = np.sum(probs * logpdfs)
    return(loglik)

if __name__ == '__main__':
    import matplotlib.pyplot as plt
    from matplotlib import colors

    n = 2000
    np.random.seed(29643)

    # Simulate 3 groups
    num_groups = 3
    mu = np.array([[0., 2.], [2., 1.], [2., 3.]])
    sigma = np.array(
        [[[1.0,  0.1], [ 0.1, 0.3]],
         [[1.0, -0.1], [-0.1, 1.0]],
         [[0.5, -0.5], [-0.5, 1.0]]])
    mix = np.array([.15, .7, .15])

    xs = [mnorm.rvs(mu[k], sigma[k], size=n) for k in range(3)]
    z = np.random.multinomial(1, mix, size=n).astype('Bool')

    x = xs[0].copy()
    x[z[:,1]] = xs[1][z[:,1]]
    x[z[:,2]] = xs[2][z[:,2]]

    z_ind = np.zeros(n)
    z_ind[z[:,1]] = 1
    z_ind[z[:,2]] = 2

    # Plot data
    cmap, norm = colors.from_levels_and_colors(
        levels=[0, 1, 2], colors=['magenta', 'cyan', 'green'], extend='max')

    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.scatter(x[:,0], x[:,1], c=z_ind, cmap=cmap, norm=norm)
    fig.savefig('./sim_data.pdf')

    # Estimate
    init_mu = np.array([[0., 0.], [1., 1.], [2., 2.]])
    init_sigma = [np.identity(2) for i in range(3)]
    init_mix = np.array([1., 1., 1.])/3

    res, (logliks, times) = em_gmm(
        x, init_mu, init_sigma, init_mix, num_iter=400)

    # Plotting
    data_mu = np.array(
        [np.mean(x[z_ind == k], axis=0) for k in range(num_groups)])
    data_sigma = np.array(
        [np.cov(x[z_ind == k], rowvar=0) for k in range(num_groups)])

    data_loglik = calc_loglik(
        x, calc_pdfs(x, data_mu, data_sigma), z)
    true_loglik = calc_loglik(x, calc_pdfs(x, mu, sigma), z)

    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(logliks)
    ax.axhline(y=data_loglik, color='green')
    ax.axhline(y=true_loglik, color='red')
    fig.savefig('./logliks.pdf')
<|endoftext|>


<commit_before>import asyncio
import discord
import time
import os
from   discord.ext import commands
from   datetime import datetime
from   operator import itemgetter
from   Cogs import Settings
from   Cogs import ReadableTime
from   Cogs import DisplayName
from   Cogs import Nullify

# This is the admin module.  It holds the admin-only commands
# Everything here *requires* that you're an admin

class Channel:

	# Init with the bot reference, and a reference to the settings var
	def __init__(self, bot, settings):
		self.bot = bot
		self.settings = settings
		self.cleanChannels = []

	async def member_update(self, before, after):
		server = after.server

		# Check if the member went offline and log the time
		if str(after.status).lower() == "offline":
			currentTime = int(time.time())
			self.settings.setUserStat(after, server, "LastOnline", currentTime)

		self.settings.checkServer(server)
		try:
			channelMOTDList = self.settings.getServerStat(server, "ChannelMOTD")
		except KeyError:
			channelMOTDList = []

		if len(channelMOTDList) > 0:
			members = 0
			membersOnline = 0
			for member in server.members:
				members += 1
				if str(member.status).lower() == "online":
					membersOnline += 1

		for id in channelMOTDList:
			channel = self.bot.get_channel(id['ID'])
			if channel:
				motd = id['MOTD'] # A markdown message of the day
				listOnline = id['ListOnline'] # Yes/No - do we list all online members or not?
				if listOnline.lower() == "yes":
					msg = '{} - ({}/{} users online)'.format(motd, int(membersOnline), int(members))
				else:
					msg = motd
				try:
					await self.bot.edit_channel(channel, topic=msg)
				except Exception:
					continue

	@commands.command(pass_context=True)
	async def islocked(self, ctx):
		"""Says whether the bot only responds to admins."""

		isLocked = self.settings.getServerStat(ctx.message.server, "AdminLock")
		if isLocked.lower() == "yes":
			msg = 'Admin lock is *On*.'
		else:
			msg = 'Admin lock is *Off*.'

		await self.bot.send_message(ctx.message.channel, msg)

	@commands.command(pass_context=True)
	async def rules(self, ctx):
		"""Display the server's rules."""
		rules = self.settings.getServerStat(ctx.message.server, "Rules")
		msg = "*{}* Rules:\n{}".format(ctx.message.server.name, rules)
		await self.bot.send_message(ctx.message.channel, msg)

	@commands.command(pass_context=True)
	async def ismuted(self, ctx, *, member = None):
		"""Says whether a member is muted in chat."""

		# Check if we're suppressing @here and @everyone mentions
		if self.settings.getServerStat(ctx.message.server, "SuppressMentions").lower() == "yes":
			suppress = True
		else:
			suppress = False

		if member == None:
			msg = 'Usage: `{}ismuted [member]`'.format(ctx.prefix)
			await self.bot.send_message(ctx.message.channel, msg)
			return

		if type(member) is str:
			memberName = member
			member = DisplayName.memberForName(memberName, ctx.message.server)
			if not member:
				msg = 'I couldn\'t find *{}*...'.format(memberName)
				# Check for suppress
				if suppress:
					msg = Nullify.clean(msg)
				await self.bot.send_message(ctx.message.channel, msg)
				return

		mutedIn = 0
		channelList = []
		for channel in ctx.message.server.channels:
			if not channel.type is discord.ChannelType.text:
				continue
			overs = channel.overwrites_for(member)
			if overs.send_messages == False:
				# We haven't been muted here yet
				overs.send_messages = False
				await self.bot.edit_channel_permissions(channel, member, overs)
				perms = member.permissions_in(channel)
				if perms.read_messages:
					mutedIn +=1
					channelList.append(channel.name)

		if len(channelList):
			# Get time remaining if needed
			#cd = self.settings.getUserStat(member, ctx.message.server, "Cooldown")
			muteList = self.setttings.getServerStat(ctx.message.server, "MuteList")
			cd = None
			for entry in muteList:
				if entry['ID'] == member.id:
					# Found them!
					cd = entry['Cooldown']

			if not cd == None:
				ct = int(time.time())
				checkRead = ReadableTime.getReadableTimeBetween(ct, cd)
				msg = '*{}* is **muted** in *{}*\n*{}* remain'.format(DisplayName.name(member), ', '.join(channelList), checkRead)
			else:
				msg = '*{}* is **muted** in *{}*.'.format(DisplayName.name(member), ', '.join(channelList))
		else:
			msg = '{} is **unmuted**.'.format(DisplayName.name(member))

		await self.bot.send_message(ctx.message.channel, msg)

	@ismuted.error
	async def ismuted_error(self, ctx, error):
		# do stuff
		msg = 'ismuted Error: {}'.format(ctx)
		await self.bot.say(msg)

	@commands.command(pass_context=True)
	async def listadmin(self, ctx):
		"""Lists admin roles and id's."""

		# Check if we're suppressing @here and @everyone mentions
		if self.settings.getServerStat(ctx.message.server, "SuppressMentions").lower() == "yes":
			suppress = True
		else:
			suppress = False

		promoArray = self.settings.getServerStat(ctx.message.server, "AdminArray")

		# rows_by_lfname = sorted(rows, key=itemgetter('lname','fname'))

		promoSorted = sorted(promoArray, key=itemgetter('Name'))

		if not len(promoSorted):
			roleText = "There are no admin roles set yet.  Use `{}addadmin [role]` to add some.".format(ctx.prefix)
			await self.bot.send_message(ctx.message.channel, roleText)
			return

		roleText = "Current Admin Roles:\n"

		for arole in promoSorted:
			found = False
			for role in ctx.message.server.roles:
				if role.id == arole["ID"]:
					# Found the role ID
					found = True
					roleText = '{}**{}** (ID : `{}`)\n'.format(roleText, role.name, arole['ID'])
			if not found:
				roleText = '{}**{}** (removed from server)\n'.format(roleText, arole['Name'])

		# Check for suppress
		if suppress:
			roleText = Nullify.clean(roleText)

		await self.bot.send_message(ctx.message.channel, roleText)

	@commands.command(pass_context=True)
	async def rolecall(self, ctx, *, role = None):
		"""Lists the number of users in a current role."""

		# Check if we're suppressing @here and @everyone mentions
		if self.settings.getServerStat(ctx.message.server, "SuppressMentions").lower() == "yes":
			suppress = True
		else:
			suppress = False

		author  = ctx.message.author
		server  = ctx.message.server
		channel = ctx.message.channel

		if role == None:
			msg = 'Usage: `{}rolecall [role]`'.format(ctx.prefix)
			await self.bot.send_message(channel, msg)
			return

		if type(role) is str:
			roleName = role
			role = DisplayName.roleForName(roleName, ctx.message.server)
			if not role:
				msg = 'I couldn\'t find *{}*...'.format(roleName)
				# Check for suppress
				if suppress:
					msg = Nullify.clean(msg)
				await self.bot.send_message(ctx.message.channel, msg)
				return

		# Create blank embed
		role_embed = discord.Embed(color=role.color)
		# Get server's icon url if one exists - otherwise grab the default blank Discord avatar
		avURL = server.icon_url
		if not len(avURL):
			avURL = discord.User.default_avatar_url
		# Add the server icon
		# role_embed.set_author(name='{}'.format(role.name), icon_url=avURL)
		role_embed.set_author(name='{}'.format(role.name))

		# We have a role
		memberCount = 0
		memberOnline = 0
		for member in server.members:
			roles = member.roles
			if role in roles:
				# We found it
				memberCount += 1
				if str(member.status).lower() == 'online':
					memberOnline+= 1

		'''if memberCount == 1:
			msg = 'There is currently *1 user* with the **{}** role.'.format(role.name)
			role_embed.add_field(name="Members", value='1 user', inline=True)
		else:
			msg = 'There are currently *{} users* with the **{}** role.'.format(memberCount, role.name)
			role_embed.add_field(name="Members", value='{}'.format(memberCount), inline=True)'''

		role_embed.add_field(name="Members", value='{} out of {} online.'.format(memberOnline, memberCount), inline=True)

		# await self.bot.send_message(channel, msg)
		await self.bot.send_message(channel, embed=role_embed)

	@rolecall.error
	async def rolecall_error(self, ctx, error):
		# do stuff
		msg = 'rolecall Error: {}'.format(ctx)
		await self.bot.say(msg)

	@commands.command(pass_context=True)
	async def log(self, ctx, messages : int = 25, *, chan : discord.Channel = None):
		"""Logs the passed number of messages from the given channel - 25 by default (admin only)."""

		author  = ctx.message.author
		server  = ctx.message.server
		channel = ctx.message.channel

		# Check for admin status
		isAdmin = author.permissions_in(channel).administrator
		if not isAdmin:
			checkAdmin = self.settings.getServerStat(server, "AdminArray")
			for role in author.roles:
				for aRole in checkAdmin:
					# Get the role that corresponds to the id
					if aRole['ID'] == role.id:
						isAdmin = True

		if not isAdmin:
			await self.bot.send_message(channel, 'You do not have sufficient privileges to access this command.')
			return

		timeStamp = datetime.today().strftime("%Y-%m-%d %H.%M")
		logFile = 'Logs-{}.txt'.format(timeStamp)

		if not chan:
			chan = channel

		# Remove original message
		await self.bot.delete_message(ctx.message)

		mess = await self.bot.send_message(ctx.message.author, 'Saving logs to *{}*...'.format(logFile))

		# Use logs_from instead of purge
		counter = 0
		msg = ''
		async for message in self.bot.logs_from(channel, limit=messages):
			counter += 1
			msg += message.content + "\n"
			msg += '----Sent-By: ' + message.author.name + '#' + message.author.discriminator + "\n"
			msg += '---------At: ' + message.timestamp.strftime("%Y-%m-%d %H.%M") + "\n"
			if message.edited_timestamp:
				msg += '--Edited-At: ' + message.edited_timestamp.strftime("%Y-%m-%d %H.%M") + "\n"
			msg += '\n'

		msg = msg[:-2].encode("utf-8")

		with open(logFile, "wb") as myfile:
			myfile.write(msg)

		message = await self.bot.edit_message(mess, 'Uploading *{}*...'.format(logFile))
		await self.bot.send_file(ctx.message.author, logFile)
		message = await self.bot.edit_message(mess, 'Uploaded *{}!*'.format(logFile))
		os.remove(logFile)

	@commands.command(pass_context=True)
	async def clean(self, ctx, messages : int = 100, *, chan : discord.Channel = None):
		"""Cleans the passed number of messages from the given channel - 100 by default, 1000 max (admin only)."""

		author  = ctx.message.author
		server  = ctx.message.server
		channel = ctx.message.channel

		# Check for admin status
		isAdmin = author.permissions_in(channel).administrator
		if not isAdmin:
			checkAdmin = self.settings.getServerStat(server, "AdminArray")
			for role in author.roles:
				for aRole in checkAdmin:
					# Get the role that corresponds to the id
					if aRole['ID'] == role.id:
						isAdmin = True

		if not isAdmin:
			await self.bot.send_message(channel, 'You do not have sufficient privileges to access this command.')
			return

		if not chan:
			chan = channel

		if chan in self.cleanChannels:
			# Don't clean messages from a channel that's being cleaned
			return

		# Remove original message
		await self.bot.delete_message(ctx.message)

		if messages > 1000:
			messages = 1000

		# Use logs_from instead of purge
		counter = 0

		# Add channel to list
		self.cleanChannels.append(ctx.message.channel)
		# I tried bulk deleting - but it doesn't work on messages over 14 days
		# old - so we're doing them individually I guess.
		totalMess = messages
		while totalMess > 0:
			gotMessage = False
			if totalMess > 100:
				tempNum = 100
			else:
				tempNum = totalMess
			try:
				async for message in self.bot.logs_from(channel, limit=tempNum):
					await self.bot.delete_message(message)
					gotMessage = True
					counter += 1
					totalMess -= 1
			except Exception:
				pass
			if not gotMessage:
				# No more messages - exit
				break

		# Remove channel from list
		self.cleanChannels.remove(ctx.message.channel)

		# Send the cleaner a pm letting them know we're done
		if counter == 1:
			await self.bot.send_message(ctx.message.author, '*1* message removed from *#{}* in *{}!*'.format(channel.name, server.name))
		else:
			await self.bot.send_message(ctx.message.author, '*{}* messages removed from *#{}* in *{}!*'.format(counter, channel.name, server.name))

		# Remove the rest
		# await self.bot.purge_from(chan, limit=messages)
<commit_msg>Typo fix setttings -> settings<commit_after>import asyncio
import discord
import time
import os
from   discord.ext import commands
from   datetime import datetime
from   operator import itemgetter
from   Cogs import Settings
from   Cogs import ReadableTime
from   Cogs import DisplayName
from   Cogs import Nullify

# This is the admin module.  It holds the admin-only commands
# Everything here *requires* that you're an admin

class Channel:

	# Init with the bot reference, and a reference to the settings var
	def __init__(self, bot, settings):
		self.bot = bot
		self.settings = settings
		self.cleanChannels = []

	async def member_update(self, before, after):
		server = after.server

		# Check if the member went offline and log the time
		if str(after.status).lower() == "offline":
			currentTime = int(time.time())
			self.settings.setUserStat(after, server, "LastOnline", currentTime)

		self.settings.checkServer(server)
		try:
			channelMOTDList = self.settings.getServerStat(server, "ChannelMOTD")
		except KeyError:
			channelMOTDList = []

		if len(channelMOTDList) > 0:
			members = 0
			membersOnline = 0
			for member in server.members:
				members += 1
				if str(member.status).lower() == "online":
					membersOnline += 1

		for id in channelMOTDList:
			channel = self.bot.get_channel(id['ID'])
			if channel:
				motd = id['MOTD'] # A markdown message of the day
				listOnline = id['ListOnline'] # Yes/No - do we list all online members or not?
				if listOnline.lower() == "yes":
					msg = '{} - ({}/{} users online)'.format(motd, int(membersOnline), int(members))
				else:
					msg = motd
				try:
					await self.bot.edit_channel(channel, topic=msg)
				except Exception:
					continue

	@commands.command(pass_context=True)
	async def islocked(self, ctx):
		"""Says whether the bot only responds to admins."""

		isLocked = self.settings.getServerStat(ctx.message.server, "AdminLock")
		if isLocked.lower() == "yes":
			msg = 'Admin lock is *On*.'
		else:
			msg = 'Admin lock is *Off*.'

		await self.bot.send_message(ctx.message.channel, msg)

	@commands.command(pass_context=True)
	async def rules(self, ctx):
		"""Display the server's rules."""
		rules = self.settings.getServerStat(ctx.message.server, "Rules")
		msg = "*{}* Rules:\n{}".format(ctx.message.server.name, rules)
		await self.bot.send_message(ctx.message.channel, msg)

	@commands.command(pass_context=True)
	async def ismuted(self, ctx, *, member = None):
		"""Says whether a member is muted in chat."""

		# Check if we're suppressing @here and @everyone mentions
		if self.settings.getServerStat(ctx.message.server, "SuppressMentions").lower() == "yes":
			suppress = True
		else:
			suppress = False

		if member == None:
			msg = 'Usage: `{}ismuted [member]`'.format(ctx.prefix)
			await self.bot.send_message(ctx.message.channel, msg)
			return

		if type(member) is str:
			memberName = member
			member = DisplayName.memberForName(memberName, ctx.message.server)
			if not member:
				msg = 'I couldn\'t find *{}*...'.format(memberName)
				# Check for suppress
				if suppress:
					msg = Nullify.clean(msg)
				await self.bot.send_message(ctx.message.channel, msg)
				return

		mutedIn = 0
		channelList = []
		for channel in ctx.message.server.channels:
			if not channel.type is discord.ChannelType.text:
				continue
			overs = channel.overwrites_for(member)
			if overs.send_messages == False:
				# We haven't been muted here yet
				overs.send_messages = False
				await self.bot.edit_channel_permissions(channel, member, overs)
				perms = member.permissions_in(channel)
				if perms.read_messages:
					mutedIn +=1
					channelList.append(channel.name)

		if len(channelList):
			# Get time remaining if needed
			#cd = self.settings.getUserStat(member, ctx.message.server, "Cooldown")
			muteList = self.settings.getServerStat(ctx.message.server, "MuteList")
			cd = None
			for entry in muteList:
				if entry['ID'] == member.id:
					# Found them!
					cd = entry['Cooldown']

			if not cd == None:
				ct = int(time.time())
				checkRead = ReadableTime.getReadableTimeBetween(ct, cd)
				msg = '*{}* is **muted** in *{}*\n*{}* remain'.format(DisplayName.name(member), ', '.join(channelList), checkRead)
			else:
				msg = '*{}* is **muted** in *{}*.'.format(DisplayName.name(member), ', '.join(channelList))
		else:
			msg = '{} is **unmuted**.'.format(DisplayName.name(member))

		await self.bot.send_message(ctx.message.channel, msg)

	@ismuted.error
	async def ismuted_error(self, ctx, error):
		# do stuff
		msg = 'ismuted Error: {}'.format(ctx)
		await self.bot.say(msg)

	@commands.command(pass_context=True)
	async def listadmin(self, ctx):
		"""Lists admin roles and id's."""

		# Check if we're suppressing @here and @everyone mentions
		if self.settings.getServerStat(ctx.message.server, "SuppressMentions").lower() == "yes":
			suppress = True
		else:
			suppress = False

		promoArray = self.settings.getServerStat(ctx.message.server, "AdminArray")

		# rows_by_lfname = sorted(rows, key=itemgetter('lname','fname'))

		promoSorted = sorted(promoArray, key=itemgetter('Name'))

		if not len(promoSorted):
			roleText = "There are no admin roles set yet.  Use `{}addadmin [role]` to add some.".format(ctx.prefix)
			await self.bot.send_message(ctx.message.channel, roleText)
			return

		roleText = "Current Admin Roles:\n"

		for arole in promoSorted:
			found = False
			for role in ctx.message.server.roles:
				if role.id == arole["ID"]:
					# Found the role ID
					found = True
					roleText = '{}**{}** (ID : `{}`)\n'.format(roleText, role.name, arole['ID'])
			if not found:
				roleText = '{}**{}** (removed from server)\n'.format(roleText, arole['Name'])

		# Check for suppress
		if suppress:
			roleText = Nullify.clean(roleText)

		await self.bot.send_message(ctx.message.channel, roleText)

	@commands.command(pass_context=True)
	async def rolecall(self, ctx, *, role = None):
		"""Lists the number of users in a current role."""

		# Check if we're suppressing @here and @everyone mentions
		if self.settings.getServerStat(ctx.message.server, "SuppressMentions").lower() == "yes":
			suppress = True
		else:
			suppress = False

		author  = ctx.message.author
		server  = ctx.message.server
		channel = ctx.message.channel

		if role == None:
			msg = 'Usage: `{}rolecall [role]`'.format(ctx.prefix)
			await self.bot.send_message(channel, msg)
			return

		if type(role) is str:
			roleName = role
			role = DisplayName.roleForName(roleName, ctx.message.server)
			if not role:
				msg = 'I couldn\'t find *{}*...'.format(roleName)
				# Check for suppress
				if suppress:
					msg = Nullify.clean(msg)
				await self.bot.send_message(ctx.message.channel, msg)
				return

		# Create blank embed
		role_embed = discord.Embed(color=role.color)
		# Get server's icon url if one exists - otherwise grab the default blank Discord avatar
		avURL = server.icon_url
		if not len(avURL):
			avURL = discord.User.default_avatar_url
		# Add the server icon
		# role_embed.set_author(name='{}'.format(role.name), icon_url=avURL)
		role_embed.set_author(name='{}'.format(role.name))

		# We have a role
		memberCount = 0
		memberOnline = 0
		for member in server.members:
			roles = member.roles
			if role in roles:
				# We found it
				memberCount += 1
				if str(member.status).lower() == 'online':
					memberOnline+= 1

		'''if memberCount == 1:
			msg = 'There is currently *1 user* with the **{}** role.'.format(role.name)
			role_embed.add_field(name="Members", value='1 user', inline=True)
		else:
			msg = 'There are currently *{} users* with the **{}** role.'.format(memberCount, role.name)
			role_embed.add_field(name="Members", value='{}'.format(memberCount), inline=True)'''

		role_embed.add_field(name="Members", value='{} out of {} online.'.format(memberOnline, memberCount), inline=True)

		# await self.bot.send_message(channel, msg)
		await self.bot.send_message(channel, embed=role_embed)

	@rolecall.error
	async def rolecall_error(self, ctx, error):
		# do stuff
		msg = 'rolecall Error: {}'.format(ctx)
		await self.bot.say(msg)

	@commands.command(pass_context=True)
	async def log(self, ctx, messages : int = 25, *, chan : discord.Channel = None):
		"""Logs the passed number of messages from the given channel - 25 by default (admin only)."""

		author  = ctx.message.author
		server  = ctx.message.server
		channel = ctx.message.channel

		# Check for admin status
		isAdmin = author.permissions_in(channel).administrator
		if not isAdmin:
			checkAdmin = self.settings.getServerStat(server, "AdminArray")
			for role in author.roles:
				for aRole in checkAdmin:
					# Get the role that corresponds to the id
					if aRole['ID'] == role.id:
						isAdmin = True

		if not isAdmin:
			await self.bot.send_message(channel, 'You do not have sufficient privileges to access this command.')
			return

		timeStamp = datetime.today().strftime("%Y-%m-%d %H.%M")
		logFile = 'Logs-{}.txt'.format(timeStamp)

		if not chan:
			chan = channel

		# Remove original message
		await self.bot.delete_message(ctx.message)

		mess = await self.bot.send_message(ctx.message.author, 'Saving logs to *{}*...'.format(logFile))

		# Use logs_from instead of purge
		counter = 0
		msg = ''
		async for message in self.bot.logs_from(channel, limit=messages):
			counter += 1
			msg += message.content + "\n"
			msg += '----Sent-By: ' + message.author.name + '#' + message.author.discriminator + "\n"
			msg += '---------At: ' + message.timestamp.strftime("%Y-%m-%d %H.%M") + "\n"
			if message.edited_timestamp:
				msg += '--Edited-At: ' + message.edited_timestamp.strftime("%Y-%m-%d %H.%M") + "\n"
			msg += '\n'

		msg = msg[:-2].encode("utf-8")

		with open(logFile, "wb") as myfile:
			myfile.write(msg)

		message = await self.bot.edit_message(mess, 'Uploading *{}*...'.format(logFile))
		await self.bot.send_file(ctx.message.author, logFile)
		message = await self.bot.edit_message(mess, 'Uploaded *{}!*'.format(logFile))
		os.remove(logFile)

	@commands.command(pass_context=True)
	async def clean(self, ctx, messages : int = 100, *, chan : discord.Channel = None):
		"""Cleans the passed number of messages from the given channel - 100 by default, 1000 max (admin only)."""

		author  = ctx.message.author
		server  = ctx.message.server
		channel = ctx.message.channel

		# Check for admin status
		isAdmin = author.permissions_in(channel).administrator
		if not isAdmin:
			checkAdmin = self.settings.getServerStat(server, "AdminArray")
			for role in author.roles:
				for aRole in checkAdmin:
					# Get the role that corresponds to the id
					if aRole['ID'] == role.id:
						isAdmin = True

		if not isAdmin:
			await self.bot.send_message(channel, 'You do not have sufficient privileges to access this command.')
			return

		if not chan:
			chan = channel

		if chan in self.cleanChannels:
			# Don't clean messages from a channel that's being cleaned
			return

		# Remove original message
		await self.bot.delete_message(ctx.message)

		if messages > 1000:
			messages = 1000

		# Use logs_from instead of purge
		counter = 0

		# Add channel to list
		self.cleanChannels.append(ctx.message.channel)
		# I tried bulk deleting - but it doesn't work on messages over 14 days
		# old - so we're doing them individually I guess.
		totalMess = messages
		while totalMess > 0:
			gotMessage = False
			if totalMess > 100:
				tempNum = 100
			else:
				tempNum = totalMess
			try:
				async for message in self.bot.logs_from(channel, limit=tempNum):
					await self.bot.delete_message(message)
					gotMessage = True
					counter += 1
					totalMess -= 1
			except Exception:
				pass
			if not gotMessage:
				# No more messages - exit
				break

		# Remove channel from list
		self.cleanChannels.remove(ctx.message.channel)

		# Send the cleaner a pm letting them know we're done
		if counter == 1:
			await self.bot.send_message(ctx.message.author, '*1* message removed from *#{}* in *{}!*'.format(channel.name, server.name))
		else:
			await self.bot.send_message(ctx.message.author, '*{}* messages removed from *#{}* in *{}!*'.format(counter, channel.name, server.name))

		# Remove the rest
		# await self.bot.purge_from(chan, limit=messages)
<|endoftext|>


<commit_before>import asyncio
import discord
import weather
from   discord.ext import commands
from   Cogs import Message

def setup(bot):
	# Add the bot
	bot.add_cog(Weather(bot))

# This is the Weather module
class Weather:

	# Init with the bot reference, and a reference to the settings var
	def __init__(self, bot):
		self.bot = bot
		self.weather = weather.Weather()

	def _get_output(self, w_text):
		if "tornado" in w_text.lower():
			return "🌪 "+w_text
		if any(x in w_text.lower() for x in ["hurricane", "tropical"]):
			return "🌀 "+w_text
		if any(x in w_text.lower() for x in ["snow", "flurries", "hail"]):
			return "🌨 "+w_text
		if "thunder" in w_text.lower():
			return "⛈ "+w_text
		if any(x in w_text.lower() for x in ["rain", "drizzle", "showers", "sleet"]):
			return "🌧 "+w_text
		if "cold" in w_text.lower():
			return "❄ "+w_text
		if any(x in w_text.lower() for x in ["windy", "blustery", "breezy"]):
			return "🌬 "+w_text
		if "mostly cloudy" in w_text.lower():
			return "⛅ "+w_text
		if "partly cloudy" in w_text.lower():
			return "🌤 "+w_text
		if "cloudy" in w_text.lower():
			return "☁ "+w_text
		if "fair" in w_text.lower():
			return "🌄 "+w_text
		if any(x in w_text.lower() for x in ["hot", "sunny", "clear"]):
			return "☀ "+w_text
		if any(x in w_text.lower() for x in ["dust", "foggy", "haze", "smoky"]):
			return "🌫 "+w_text
		return w_text

	@commands.command(pass_context=True)
	async def forecast(self, ctx, *, location = None):
		"""Gets some weather."""
		if location == None:
			await ctx.send("You need to pass a city.")
			return
		location = self.weather.lookup_by_location(location)
		location_info = location.location()
		title = "{}, {} ({})".format(location_info['city'], location_info['country'], location_info['region'][1:])
		current = "__**Current Weather**__:\n\n{}, {} °F\n\n__**Future Forecast:**__\n\n".format(self._get_output(location.condition().text()), location.condition().temp())
		fields = []
		for f in location.forecast():
			fields.append({ "name" : f.date(), "value" : self._get_output(f.text()) + ", {}/{} °F".format(f.high(), f.low()), "inline" : False })
		await Message.Embed(title=title, description=current, fields=fields, color=ctx.author).send(ctx)<commit_msg>Fix for incorrect city<commit_after>import asyncio
import discord
import weather
from   discord.ext import commands
from   Cogs import Message

def setup(bot):
	# Add the bot
	bot.add_cog(Weather(bot))

# This is the Weather module
class Weather:

	# Init with the bot reference, and a reference to the settings var
	def __init__(self, bot):
		self.bot = bot
		self.weather = weather.Weather()

	def _get_output(self, w_text):
		if "tornado" in w_text.lower():
			return "🌪 "+w_text
		if any(x in w_text.lower() for x in ["hurricane", "tropical"]):
			return "🌀 "+w_text
		if any(x in w_text.lower() for x in ["snow", "flurries", "hail"]):
			return "🌨 "+w_text
		if "thunder" in w_text.lower():
			return "⛈ "+w_text
		if any(x in w_text.lower() for x in ["rain", "drizzle", "showers", "sleet"]):
			return "🌧 "+w_text
		if "cold" in w_text.lower():
			return "❄ "+w_text
		if any(x in w_text.lower() for x in ["windy", "blustery", "breezy"]):
			return "🌬 "+w_text
		if "mostly cloudy" in w_text.lower():
			return "⛅ "+w_text
		if "partly cloudy" in w_text.lower():
			return "🌤 "+w_text
		if "cloudy" in w_text.lower():
			return "☁ "+w_text
		if "fair" in w_text.lower():
			return "🌄 "+w_text
		if any(x in w_text.lower() for x in ["hot", "sunny", "clear"]):
			return "☀ "+w_text
		if any(x in w_text.lower() for x in ["dust", "foggy", "haze", "smoky"]):
			return "🌫 "+w_text
		return w_text

	@commands.command(pass_context=True)
	async def forecast(self, ctx, *, location = None):
		"""Gets some weather."""
		if location == None:
			await ctx.send("Usage: `{}forecast [city name]`".format(ctx.prefix))
			return
		location = self.weather.lookup_by_location(location)
		if not location:
			await ctx.send("I couldn't find that city...")
			return
		location_info = location.location()
		title = "{}, {} ({})".format(location_info['city'], location_info['country'], location_info['region'][1:])
		current = "__**Current Weather**__:\n\n{}, {} °F\n\n__**Future Forecast:**__\n\n".format(self._get_output(location.condition().text()), location.condition().temp())
		fields = []
		for f in location.forecast():
			fields.append({ "name" : f.date(), "value" : self._get_output(f.text()) + ", {}/{} °F".format(f.high(), f.low()), "inline" : False })
		await Message.Embed(title=title, description=current, fields=fields, color=ctx.author).send(ctx)<|endoftext|>


<commit_before># Copyright 2020, 2021, 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Contains workspace functions to use Emacs Lisp rules."""

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

def rules_elisp_dependencies():
    """Installs necessary dependencies for Emacs Lisp rules.

    Call this function in your `WORKSPACE` file.
    """
    http_archive(
        name = "gnu_emacs_27.1",
        build_file_content = _build_file(macos_arm = False),
        sha256 = "4a4c128f915fc937d61edfc273c98106711b540c9be3cd5d2e2b9b5b2f172e41",
        strip_prefix = "emacs-27.1/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-27.1.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-27.1.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_27.2",
        build_file_content = _build_file(macos_arm = True),
        sha256 = "b4a7cc4e78e63f378624e0919215b910af5bb2a0afc819fad298272e9f40c1b9",
        strip_prefix = "emacs-27.2/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-27.2.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-27.2.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_28.1",
        build_file_content = _build_file(macos_arm = True),
        sha256 = "28b1b3d099037a088f0a4ca251d7e7262eab5ea1677aabffa6c4426961ad75e1",
        strip_prefix = "emacs-28.1/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-28.1.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-28.1.tar.xz",
        ],
    )
    http_archive(
        name = "bazel_skylib",
        sha256 = "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728",
        urls = [
            "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz",
            "https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz",
        ],
    )
    http_archive(
        name = "com_google_absl",
        sha256 = "a4567ff02faca671b95e31d315bab18b42b6c6f1a60e91c6ea84e5a2142112c2",
        strip_prefix = "abseil-cpp-20211102.0/",
        urls = [
            "https://github.com/abseil/abseil-cpp/archive/refs/tags/20211102.0.zip",  # 2021-11-03
        ],
    )
    http_archive(
        name = "com_google_protobuf",
        sha256 = "8b28fdd45bab62d15db232ec404248901842e5340299a57765e48abe8a80d930",
        strip_prefix = "protobuf-3.20.1/",
        urls = [
            "https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.20.1.tar.gz",  # 2022-04-22
        ],
    )
    http_archive(
        name = "upb",
        sha256 = "2e32c87d3beda5cb403e358b7a499342df6166488bf96e496ed152b1d44166ac",
        strip_prefix = "upb-3e0890c055e9816204f35ef10f6926fb42a553f7/",
        urls = [
            "https://github.com/protocolbuffers/upb/archive/3e0890c055e9816204f35ef10f6926fb42a553f7.zip",  # 2022-04-20
        ],
    )
    _toolchains(name = "phst_rules_elisp_toolchains")

# buildifier: disable=unnamed-macro
def rules_elisp_toolchains():
    """Registers the default toolchains for Emacs Lisp."""
    native.register_toolchains("@phst_rules_elisp//elisp:hermetic_toolchain")

def _toolchains_impl(repository_ctx):
    if repository_ctx.os.name.startswith("windows"):
        content = _WINDOWS_TOOLCHAINS
    else:
        content = _UNIX_TOOLCHAINS
    repository_ctx.file("BUILD", content)

_toolchains = repository_rule(
    implementation = _toolchains_impl,
)

def _build_file(macos_arm):
    return _BUILD_TEMPLATE.format(
        module_header = '"emacs-module.h"',
        dump_mode = "portable",
        windows = "",
        macos_x86 = "",
        macos_arm = "" if macos_arm else '"@phst_rules_elisp//emacs:incompatible"',
    )

_BUILD_TEMPLATE = """
load("@phst_rules_elisp//emacs:defs.bzl", "emacs_binary")

emacs_binary(
    name = "emacs",
    srcs = glob(["**"]),
    builtin_features = "builtin_features.json",
    dump_mode = "{dump_mode}",
    module_header = {module_header},
    readme = "README",
    target_compatible_with = select({{
        "@phst_rules_elisp//emacs:always_supported": [],
        "@phst_rules_elisp//emacs:windows": [{windows}],
        "@phst_rules_elisp//emacs:macos_arm": [{macos_arm}],
        "@phst_rules_elisp//emacs:macos_x86": [{macos_x86}],
        "//conditions:default": ["@phst_rules_elisp//emacs:incompatible"],
    }}),
    visibility = ["@phst_rules_elisp//emacs:__pkg__"],
)

cc_library(
    name = "module_header",
    srcs = ["emacs-module.h"],
    visibility = ["@phst_rules_elisp//emacs:__pkg__"],
)

filegroup(
    name = "builtin_features",
    srcs = ["builtin_features.json"],
    visibility = ["@phst_rules_elisp//emacs:__pkg__"],
)
"""

_UNIX_TOOLCHAINS = """
alias(
    name = "emacs_cc_toolchain",
    actual = "@bazel_tools//tools/cpp:current_cc_toolchain",
    visibility = [
        "@gnu_emacs_27.1//:__pkg__",
        "@gnu_emacs_27.2//:__pkg__",
        "@gnu_emacs_28.1//:__pkg__",
    ],
)
"""

_WINDOWS_TOOLCHAINS = """
cc_toolchain_suite(
    name = "emacs_cc_toolchain",
    toolchains = {
        "x64_windows": "@local_config_cc//:cc-compiler-x64_windows_mingw",
        "x64_windows|mingw-gcc": "@local_config_cc//:cc-compiler-x64_windows_mingw",
    },
    visibility = [
        "@gnu_emacs_27.1//:__pkg__",
        "@gnu_emacs_27.2//:__pkg__",
        "@gnu_emacs_28.1//:__pkg__",
    ],
)
"""
<commit_msg>Update µpb dependency<commit_after># Copyright 2020, 2021, 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Contains workspace functions to use Emacs Lisp rules."""

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

def rules_elisp_dependencies():
    """Installs necessary dependencies for Emacs Lisp rules.

    Call this function in your `WORKSPACE` file.
    """
    http_archive(
        name = "gnu_emacs_27.1",
        build_file_content = _build_file(macos_arm = False),
        sha256 = "4a4c128f915fc937d61edfc273c98106711b540c9be3cd5d2e2b9b5b2f172e41",
        strip_prefix = "emacs-27.1/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-27.1.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-27.1.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_27.2",
        build_file_content = _build_file(macos_arm = True),
        sha256 = "b4a7cc4e78e63f378624e0919215b910af5bb2a0afc819fad298272e9f40c1b9",
        strip_prefix = "emacs-27.2/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-27.2.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-27.2.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_28.1",
        build_file_content = _build_file(macos_arm = True),
        sha256 = "28b1b3d099037a088f0a4ca251d7e7262eab5ea1677aabffa6c4426961ad75e1",
        strip_prefix = "emacs-28.1/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-28.1.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-28.1.tar.xz",
        ],
    )
    http_archive(
        name = "bazel_skylib",
        sha256 = "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728",
        urls = [
            "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz",
            "https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz",
        ],
    )
    http_archive(
        name = "com_google_absl",
        sha256 = "a4567ff02faca671b95e31d315bab18b42b6c6f1a60e91c6ea84e5a2142112c2",
        strip_prefix = "abseil-cpp-20211102.0/",
        urls = [
            "https://github.com/abseil/abseil-cpp/archive/refs/tags/20211102.0.zip",  # 2021-11-03
        ],
    )
    http_archive(
        name = "com_google_protobuf",
        sha256 = "8b28fdd45bab62d15db232ec404248901842e5340299a57765e48abe8a80d930",
        strip_prefix = "protobuf-3.20.1/",
        urls = [
            "https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.20.1.tar.gz",  # 2022-04-22
        ],
    )
    http_archive(
        name = "upb",
        sha256 = "13650f365c8f66b71e1da27ab7c59ddde1c52ab4fca19629c4970b7d4e6b935c",
        strip_prefix = "upb-c7620a4690272eab892bed56b2617b7f841bdc82/",
        urls = [
            "https://github.com/protocolbuffers/upb/archive/c7620a4690272eab892bed56b2617b7f841bdc82.zip",  # 2022-04-29
        ],
    )
    _toolchains(name = "phst_rules_elisp_toolchains")

# buildifier: disable=unnamed-macro
def rules_elisp_toolchains():
    """Registers the default toolchains for Emacs Lisp."""
    native.register_toolchains("@phst_rules_elisp//elisp:hermetic_toolchain")

def _toolchains_impl(repository_ctx):
    if repository_ctx.os.name.startswith("windows"):
        content = _WINDOWS_TOOLCHAINS
    else:
        content = _UNIX_TOOLCHAINS
    repository_ctx.file("BUILD", content)

_toolchains = repository_rule(
    implementation = _toolchains_impl,
)

def _build_file(macos_arm):
    return _BUILD_TEMPLATE.format(
        module_header = '"emacs-module.h"',
        dump_mode = "portable",
        windows = "",
        macos_x86 = "",
        macos_arm = "" if macos_arm else '"@phst_rules_elisp//emacs:incompatible"',
    )

_BUILD_TEMPLATE = """
load("@phst_rules_elisp//emacs:defs.bzl", "emacs_binary")

emacs_binary(
    name = "emacs",
    srcs = glob(["**"]),
    builtin_features = "builtin_features.json",
    dump_mode = "{dump_mode}",
    module_header = {module_header},
    readme = "README",
    target_compatible_with = select({{
        "@phst_rules_elisp//emacs:always_supported": [],
        "@phst_rules_elisp//emacs:windows": [{windows}],
        "@phst_rules_elisp//emacs:macos_arm": [{macos_arm}],
        "@phst_rules_elisp//emacs:macos_x86": [{macos_x86}],
        "//conditions:default": ["@phst_rules_elisp//emacs:incompatible"],
    }}),
    visibility = ["@phst_rules_elisp//emacs:__pkg__"],
)

cc_library(
    name = "module_header",
    srcs = ["emacs-module.h"],
    visibility = ["@phst_rules_elisp//emacs:__pkg__"],
)

filegroup(
    name = "builtin_features",
    srcs = ["builtin_features.json"],
    visibility = ["@phst_rules_elisp//emacs:__pkg__"],
)
"""

_UNIX_TOOLCHAINS = """
alias(
    name = "emacs_cc_toolchain",
    actual = "@bazel_tools//tools/cpp:current_cc_toolchain",
    visibility = [
        "@gnu_emacs_27.1//:__pkg__",
        "@gnu_emacs_27.2//:__pkg__",
        "@gnu_emacs_28.1//:__pkg__",
    ],
)
"""

_WINDOWS_TOOLCHAINS = """
cc_toolchain_suite(
    name = "emacs_cc_toolchain",
    toolchains = {
        "x64_windows": "@local_config_cc//:cc-compiler-x64_windows_mingw",
        "x64_windows|mingw-gcc": "@local_config_cc//:cc-compiler-x64_windows_mingw",
    },
    visibility = [
        "@gnu_emacs_27.1//:__pkg__",
        "@gnu_emacs_27.2//:__pkg__",
        "@gnu_emacs_28.1//:__pkg__",
    ],
)
"""
<|endoftext|>


<commit_before># Copyright 2020, 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Contains workspace functions to use Emacs Lisp rules."""

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

def rules_elisp_dependencies():
    """Installs necessary dependencies for Emacs Lisp rules.

    Call this function in your `WORKSPACE` file.
    """
    http_archive(
        name = "gnu_emacs_26.1",
        build_file_content = _build_file(macos_arm = False, portable = False),
        sha256 = "1cf4fc240cd77c25309d15e18593789c8dbfba5c2b44d8f77c886542300fd32c",
        strip_prefix = "emacs-26.1/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-26.1.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-26.1.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_26.2",
        build_file_content = _build_file(macos_arm = False, portable = False),
        sha256 = "151ce69dbe5b809d4492ffae4a4b153b2778459de6deb26f35691e1281a9c58e",
        strip_prefix = "emacs-26.2/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-26.2.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-26.2.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_26.3",
        build_file_content = _build_file(macos_arm = False, portable = False),
        sha256 = "4d90e6751ad8967822c6e092db07466b9d383ef1653feb2f95c93e7de66d3485",
        strip_prefix = "emacs-26.3/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-26.3.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-26.3.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_27.1",
        build_file_content = _build_file(macos_arm = False, portable = True),
        sha256 = "4a4c128f915fc937d61edfc273c98106711b540c9be3cd5d2e2b9b5b2f172e41",
        strip_prefix = "emacs-27.1/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-27.1.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-27.1.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_27.2",
        build_file_content = _build_file(macos_arm = True, portable = True),
        sha256 = "b4a7cc4e78e63f378624e0919215b910af5bb2a0afc819fad298272e9f40c1b9",
        strip_prefix = "emacs-27.2/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-27.2.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-27.2.tar.xz",
        ],
    )
    http_archive(
        name = "bazel_skylib",
        sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c",
        urls = [
            "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
            "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
        ],
    )
    http_archive(
        name = "com_google_absl",
        sha256 = "6622893ab117501fc23268a2936e0d46ee6cb0319dcf2275e33a708cd9634ea6",
        strip_prefix = "abseil-cpp-20200923.3",
        urls = ["https://github.com/abseil/abseil-cpp/archive/refs/tags/20200923.3.zip"],
    )
    http_archive(
        name = "nlohmann_json",
        build_file = "@phst_rules_elisp//:json.BUILD",
        sha256 = "5b9b819aed31626aefe2eace23498cafafc1691890556cd36d2a8002f6905009",
        strip_prefix = "single_include",
        urls = ["https://github.com/nlohmann/json/releases/download/v3.9.0/include.zip"],
    )

def rules_elisp_toolchains():
    """Registers the default toolchains for Emacs Lisp."""
    native.register_toolchains("@phst_rules_elisp//elisp:hermetic_toolchain")

def _build_file(portable, macos_arm):
    # The “target_compatible_with” attribute requires Bazel 4.
    major, dot, rest = native.bazel_version.partition(".")
    target_compatible_with = _TARGET_COMPATIBLE_WITH_TEMPLATE.format(
        macos_arm = "" if macos_arm else '"@phst_rules_elisp//emacs:incompatible"',
    ) if int(major) >= 4 else ""
    return _BUILD_TEMPLATE.format(
        module_header = '"emacs-module.h"' if portable else "None",
        dump_mode = "portable" if portable else "unexec",
        target_compatible_with = target_compatible_with,
    )

_BUILD_TEMPLATE = """
load("@phst_rules_elisp//emacs:defs.bzl", "emacs_binary")

emacs_binary(
    name = "emacs",
    srcs = glob(["**"]),
    dump_mode = "{dump_mode}",
    module_header = {module_header},
    readme = "README",
    {target_compatible_with}
    visibility = ["@phst_rules_elisp//emacs:__pkg__"],
)

cc_library(
    name = "module_header",
    srcs = ["emacs-module.h"],
    visibility = ["@phst_rules_elisp//emacs:__pkg__"],
)
"""

_TARGET_COMPATIBLE_WITH_TEMPLATE = """
    target_compatible_with = select({{
        "@phst_rules_elisp//emacs:always_supported": [],
        "@phst_rules_elisp//emacs:macos_arm": [{macos_arm}],
        "//conditions:default": ["@phst_rules_elisp//emacs:incompatible"],
    }}),
"""
<commit_msg>Update to newest Abseil LTS release<commit_after># Copyright 2020, 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Contains workspace functions to use Emacs Lisp rules."""

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

def rules_elisp_dependencies():
    """Installs necessary dependencies for Emacs Lisp rules.

    Call this function in your `WORKSPACE` file.
    """
    http_archive(
        name = "gnu_emacs_26.1",
        build_file_content = _build_file(macos_arm = False, portable = False),
        sha256 = "1cf4fc240cd77c25309d15e18593789c8dbfba5c2b44d8f77c886542300fd32c",
        strip_prefix = "emacs-26.1/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-26.1.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-26.1.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_26.2",
        build_file_content = _build_file(macos_arm = False, portable = False),
        sha256 = "151ce69dbe5b809d4492ffae4a4b153b2778459de6deb26f35691e1281a9c58e",
        strip_prefix = "emacs-26.2/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-26.2.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-26.2.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_26.3",
        build_file_content = _build_file(macos_arm = False, portable = False),
        sha256 = "4d90e6751ad8967822c6e092db07466b9d383ef1653feb2f95c93e7de66d3485",
        strip_prefix = "emacs-26.3/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-26.3.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-26.3.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_27.1",
        build_file_content = _build_file(macos_arm = False, portable = True),
        sha256 = "4a4c128f915fc937d61edfc273c98106711b540c9be3cd5d2e2b9b5b2f172e41",
        strip_prefix = "emacs-27.1/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-27.1.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-27.1.tar.xz",
        ],
    )
    http_archive(
        name = "gnu_emacs_27.2",
        build_file_content = _build_file(macos_arm = True, portable = True),
        sha256 = "b4a7cc4e78e63f378624e0919215b910af5bb2a0afc819fad298272e9f40c1b9",
        strip_prefix = "emacs-27.2/",
        urls = [
            "https://ftpmirror.gnu.org/emacs/emacs-27.2.tar.xz",
            "https://ftp.gnu.org/gnu/emacs/emacs-27.2.tar.xz",
        ],
    )
    http_archive(
        name = "bazel_skylib",
        sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c",
        urls = [
            "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
            "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
        ],
    )
    http_archive(
        name = "com_google_absl",
        sha256 = "59b862f50e710277f8ede96f083a5bb8d7c9595376146838b9580be90374ee1f",
        strip_prefix = "abseil-cpp-20210324.2/",
        urls = ["https://github.com/abseil/abseil-cpp/archive/refs/tags/20210324.2.tar.gz"],
    )
    http_archive(
        name = "nlohmann_json",
        build_file = "@phst_rules_elisp//:json.BUILD",
        sha256 = "5b9b819aed31626aefe2eace23498cafafc1691890556cd36d2a8002f6905009",
        strip_prefix = "single_include",
        urls = ["https://github.com/nlohmann/json/releases/download/v3.9.0/include.zip"],
    )

def rules_elisp_toolchains():
    """Registers the default toolchains for Emacs Lisp."""
    native.register_toolchains("@phst_rules_elisp//elisp:hermetic_toolchain")

def _build_file(portable, macos_arm):
    # The “target_compatible_with” attribute requires Bazel 4.
    major, dot, rest = native.bazel_version.partition(".")
    target_compatible_with = _TARGET_COMPATIBLE_WITH_TEMPLATE.format(
        macos_arm = "" if macos_arm else '"@phst_rules_elisp//emacs:incompatible"',
    ) if int(major) >= 4 else ""
    return _BUILD_TEMPLATE.format(
        module_header = '"emacs-module.h"' if portable else "None",
        dump_mode = "portable" if portable else "unexec",
        target_compatible_with = target_compatible_with,
    )

_BUILD_TEMPLATE = """
load("@phst_rules_elisp//emacs:defs.bzl", "emacs_binary")

emacs_binary(
    name = "emacs",
    srcs = glob(["**"]),
    dump_mode = "{dump_mode}",
    module_header = {module_header},
    readme = "README",
    {target_compatible_with}
    visibility = ["@phst_rules_elisp//emacs:__pkg__"],
)

cc_library(
    name = "module_header",
    srcs = ["emacs-module.h"],
    visibility = ["@phst_rules_elisp//emacs:__pkg__"],
)
"""

_TARGET_COMPATIBLE_WITH_TEMPLATE = """
    target_compatible_with = select({{
        "@phst_rules_elisp//emacs:always_supported": [],
        "@phst_rules_elisp//emacs:macos_arm": [{macos_arm}],
        "//conditions:default": ["@phst_rules_elisp//emacs:incompatible"],
    }}),
"""
<|endoftext|>