<commit_before>###
# Copyright (c) 2002-2004, Jeremiah Fincher
# Copyright (c) 2008-2010, James McCoy
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions, and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions, and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   * Neither the name of the author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###

import re
import sys
import cgi
import json
import time
import socket
import urllib

import supybot.conf as conf
import supybot.utils as utils
import supybot.world as world
from supybot.commands import *
import supybot.ircmsgs as ircmsgs
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('Google')

import json

class Google(callbacks.PluginRegexp):
    threaded = True
    callBefore = ['Web']
    regexps = ['googleSnarfer']

    _colorGoogles = {}
    def _getColorGoogle(self, m):
        s = m.group(1)
        ret = self._colorGoogles.get(s)
        if not ret:
            L = list(s)
            L[0] = ircutils.mircColor(L[0], 'blue')[:-1]
            L[1] = ircutils.mircColor(L[1], 'red')[:-1]
            L[2] = ircutils.mircColor(L[2], 'yellow')[:-1]
            L[3] = ircutils.mircColor(L[3], 'blue')[:-1]
            L[4] = ircutils.mircColor(L[4], 'green')[:-1]
            L[5] = ircutils.mircColor(L[5], 'red')
            ret = ''.join(L)
            self._colorGoogles[s] = ret
        return ircutils.bold(ret)

    _googleRe = re.compile(r'\b(google)\b', re.I)
    def outFilter(self, irc, msg):
        if msg.command == 'PRIVMSG' and \
           self.registryValue('colorfulFilter', msg.args[0]):
            s = msg.args[1]
            s = re.sub(self._googleRe, self._getColorGoogle, s)
            msg = ircmsgs.privmsg(msg.args[0], s, msg=msg)
        return msg

    _gsearchUrl = 'http://ajax.googleapis.com/ajax/services/search/web'
    @internationalizeDocstring
    def search(self, query, channel, options={}):
        """Perform a search using Google's AJAX API.
        search("search phrase", options={})

        Valid options are:
            smallsearch - True/False (Default: False)
            filter - {active,moderate,off} (Default: "moderate")
            language - Restrict search to documents in the given language
                       (Default: "lang_en")
        """
        ref = self.registryValue('referer')
        if not ref:
            ref = 'http://%s/%s' % (dynamic.irc.server,
                                    dynamic.irc.nick)
        headers = utils.web.defaultHeaders
        headers['Referer'] = ref
        opts = {'q': query, 'v': '1.0'}
        for (k, v) in options.iteritems():
            if k == 'smallsearch':
                if v:
                    opts['rsz'] = 'small'
                else:
                    opts['rsz'] = 'large'
            elif k == 'filter':
                opts['safe'] = v
            elif k == 'language':
                opts['lr'] = v
        defLang = self.registryValue('defaultLanguage', channel)
        if 'lr' not in opts and defLang:
            opts['lr'] = defLang
        if 'safe' not in opts:
            opts['safe'] = self.registryValue('searchFilter', dynamic.channel)
        if 'rsz' not in opts:
            opts['rsz'] = 'large'

        text = utils.web.getUrl('%s?%s' % (self._gsearchUrl,
                                           urllib.urlencode(opts)),
                                headers=headers).decode('utf8')
        data = json.loads(text)
        if data['responseStatus'] != 200:
            raise callbacks.Error, _('We broke The Google!')
        return data

    def formatData(self, data, bold=True, max=0, onetoone=False):
        if isinstance(data, basestring):
            return data
        results = []
        if max:
            data = data[:max]
        for result in data:
            title = utils.web.htmlToText(result['titleNoFormatting']\
                                         .encode('utf-8'))
            url = result['unescapedUrl']
            if sys.version_info[0] < 3:
                url = url.encode('utf-8')
            if title:
                if bold:
                    title = ircutils.bold(title)
                results.append(format('%s: %u', title, url))
            else:
                results.append(url)
        if sys.version_info[0] < 3:
            repl = lambda x:x if isinstance(x, unicode) else unicode(x, 'utf8')
            results = map(repl, results)
        if not results:
            return [_('No matches found.')]
        elif onetoone:
            return results
        else:
            return [u'; '.join(results)]

    @internationalizeDocstring
    def lucky(self, irc, msg, args, opts, text):
        """[--snippet] <search>

        Does a google search, but only returns the first result.
        If option --snippet is given, returns also the page text snippet.
        """
        opts = dict(opts)
        data = self.search(text, msg.args[0], {'smallsearch': True})
        if data['responseData']['results']:
            url = data['responseData']['results'][0]['unescapedUrl']
            if opts.has_key('snippet'):
                snippet = data['responseData']['results'][0]['content']
                snippet = " | " + utils.web.htmlToText(snippet, tagReplace='')
            else:
                snippet = ""
            result = url + snippet
            irc.reply(result)
        else:
            irc.reply(_('Google found nothing.'))
    lucky = wrap(lucky, [getopts({'snippet':'',}), 'text'])

    @internationalizeDocstring
    def google(self, irc, msg, args, optlist, text):
        """<search> [--{filter,language} <value>]

        Searches google.com for the given string.  As many results as can fit
        are included.  --language accepts a language abbreviation; --filter
        accepts a filtering level ('active', 'moderate', 'off').
        """
        if 'language' in optlist and optlist['language'].lower() not in \
           conf.supybot.plugins.Google.safesearch.validStrings:
            irc.errorInvalid('language')
        data = self.search(text, msg.args[0], dict(optlist))
        if data['responseStatus'] != 200:
            irc.reply(_('We broke The Google!'))
            return
        bold = self.registryValue('bold', msg.args[0])
        max = self.registryValue('maximumResults', msg.args[0])
        # We don't use supybot.reply.oneToOne here, because you generally
        # do not want @google to echo ~20 lines of results, even if you
        # have reply.oneToOne enabled.
        onetoone = self.registryValue('oneToOne', msg.args[0])
        for result in self.formatData(data['responseData']['results'],
                                  bold=bold, max=max, onetoone=onetoone):
            irc.reply(result)
    google = wrap(google, [getopts({'language':'something',
                                    'filter':''}),
                           'text'])

    @internationalizeDocstring
    def cache(self, irc, msg, args, url):
        """<url>

        Returns a link to the cached version of <url> if it is available.
        """
        data = self.search(url, msg.args[0], {'smallsearch': True})
        if data['responseData']['results']:
            m = data['responseData']['results'][0]
            if m['cacheUrl']:
                url = m['cacheUrl'].encode('utf-8')
                irc.reply(url)
                return
        irc.error(_('Google seems to have no cache for that site.'))
    cache = wrap(cache, ['url'])

    @internationalizeDocstring
    def fight(self, irc, msg, args):
        """<search string> <search string> [<search string> ...]

        Returns the results of each search, in order, from greatest number
        of results to least.
        """
        channel = msg.args[0]
        results = []
        for arg in args:
            data = self.search(arg, channel, {'smallsearch': True})
            count = data['responseData']['cursor'].get('estimatedResultCount',
                                                       0)
            results.append((int(count), arg))
        results.sort()
        results.reverse()
        if self.registryValue('bold', msg.args[0]):
            bold = ircutils.bold
        else:
            bold = repr
        s = ', '.join([format('%s: %i', bold(s), i) for (i, s) in results])
        irc.reply(s)

    @internationalizeDocstring
    def translate(self, irc, msg, args, sourceLang, targetLang, text):
        """<source language> [to] <target language> <text>

        Returns <text> translated from <source language> into <target
        language>.
        """

        channel = msg.args[0]

        headers = utils.web.defaultHeaders
        headers['User-Agent'] = ('Mozilla/5.0 (X11; U; Linux i686) '
                                 'Gecko/20071127 Firefox/2.0.0.11')

        sourceLang = urllib.quote(sourceLang)
        targetLang = urllib.quote(targetLang)

        text = urllib.quote(text)

        result = utils.web.getUrlFd('http://translate.google.com/translate_a/t'
                                    '?client=t&hl=en&sl=%s&tl=%s&multires=1'
                                    '&otf=1&ssel=0&tsel=0&uptl=en&sc=1&text='
                                    '%s' % (sourceLang, targetLang, text),
                                    headers).read().decode('utf8')

        while ',,' in result:
            result = result.replace(',,', ',null,')
        data = json.loads(result)

        try:
            language = data[2]
        except:
            language = 'unknown'

        irc.reply(''.join(x[0] for x in data[0]), language)
    translate = wrap(translate, ['something', 'to', 'something', 'text'])

    def googleSnarfer(self, irc, msg, match):
        r"^google\s+(.*)$"
        if not self.registryValue('searchSnarfer', msg.args[0]):
            return
        searchString = match.group(1)
        data = self.search(searchString, msg.args[0], {'smallsearch': True})
        if data['responseData']['results']:
            url = data['responseData']['results'][0]['unescapedUrl']
            irc.reply(url, prefixNick=False)
    googleSnarfer = urlSnarfer(googleSnarfer)

    def _googleUrl(self, s, channel):
        s = urllib.quote_plus(s)
        url = r'http://%s/search?q=%s' % \
                (self.registryValue('baseUrl', channel), s)
        return url

    def _googleUrlIG(self, s, channel):
        s = urllib.quote_plus(s)
        url = r'http://%s/ig/calculator?hl=en&q=%s' % \
                (self.registryValue('baseUrl', channel), s)
        return url

    _calcRe1 = re.compile(r'
]*>(.*?)
', re.I)
    _calcRe2 = re.compile(r'<h\d class="?r"?[^>]*>(?:)?(.*?)(?:)?</h\d>', re.I | re.S)
    _calcSupRe = re.compile(r'(.*?)', re.I)
    _calcFontRe = re.compile(r'(.*?)')
    _calcTimesRe = re.compile(r'&(?:times|#215);')
    @internationalizeDocstring
    def calc(self, irc, msg, args, expr):
        """<expression>

        Uses Google's calculator to calculate the value of <expression>.
        """
        channel = msg.args[0]
        if not ircutils.isChannel(channel):
            channel = None
        urlig = self._googleUrlIG(expr, channel)
        js = utils.web.getUrl(urlig).decode('utf8')
        # Convert JavaScript to JSON. Ouch.
        js = js \
                .replace('lhs:','"lhs":') \
                .replace('rhs:','"rhs":') \
                .replace('error:','"error":') \
                .replace('icc:','"icc":') \
                .replace('\\', '\\\\')
        js = json.loads(js)

        url = self._googleUrl(expr, channel)
        html = utils.web.getUrl(url).decode('utf8')
        match = self._calcRe1.search(html)
        if match is None:
            match = self._calcRe2.search(html)
        if match is not None:
            s = match.group(1)
            s = self._calcSupRe.sub(r'^(\1)', s)
            s = self._calcFontRe.sub(r',', s)
            s = self._calcTimesRe.sub(r'*', s)
            s = utils.web.htmlToText(s)
            if ' = ' in s: # Extra check, since the regex seems to fail.
                irc.reply(s)
                return
            elif js['lhs'] and js['rhs']:
                # Outputs the original result. Might look ugly.
                irc.reply("%s = %s" % (js['lhs'], js['rhs'],))
                return
        irc.reply(_('Google says: Error: %s.') % (js['error'],))
        irc.reply('Google\'s calculator didn\'t come up with anything.')
    calc = wrap(calc, ['text'])

    _phoneRe = re.compile(r'Phonebook.*?(.*?)<a href')
    @internationalizeDocstring
    def phonebook(self, irc, msg, args, phonenumber):
        """<phone number>

        Looks <phone number> up on Google.
        """
        channel = msg.args[0]
        if not ircutils.isChannel(channel):
            channel = None
        url = self._googleUrl(phonenumber, channel)
        html = utils.web.getUrl(url).decode('utf8')
        m = self._phoneRe.search(html)
        if m is not None:
            s = m.group(1)
            s = s.replace('', '')
            s = s.replace('', '')
            s = utils.web.htmlToText(s)
            irc.reply(s)
        else:
            irc.reply(_('Google\'s phonebook didn\'t come up with anything.'))
    phonebook = wrap(phonebook, ['text'])

Class = Google

# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
<commit_msg>Google: Filter out [, before attempting to interpret Googles response as json.<commit_after>###
# Copyright (c) 2002-2004, Jeremiah Fincher
# Copyright (c) 2008-2010, James McCoy
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions, and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions, and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   * Neither the name of the author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###

import re
import sys
import cgi
import json
import time
import socket
import urllib

import supybot.conf as conf
import supybot.utils as utils
import supybot.world as world
from supybot.commands import *
import supybot.ircmsgs as ircmsgs
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('Google')

import json

class Google(callbacks.PluginRegexp):
    threaded = True
    callBefore = ['Web']
    regexps = ['googleSnarfer']

    _colorGoogles = {}
    def _getColorGoogle(self, m):
        s = m.group(1)
        ret = self._colorGoogles.get(s)
        if not ret:
            L = list(s)
            L[0] = ircutils.mircColor(L[0], 'blue')[:-1]
            L[1] = ircutils.mircColor(L[1], 'red')[:-1]
            L[2] = ircutils.mircColor(L[2], 'yellow')[:-1]
            L[3] = ircutils.mircColor(L[3], 'blue')[:-1]
            L[4] = ircutils.mircColor(L[4], 'green')[:-1]
            L[5] = ircutils.mircColor(L[5], 'red')
            ret = ''.join(L)
            self._colorGoogles[s] = ret
        return ircutils.bold(ret)

    _googleRe = re.compile(r'\b(google)\b', re.I)
    def outFilter(self, irc, msg):
        if msg.command == 'PRIVMSG' and \
           self.registryValue('colorfulFilter', msg.args[0]):
            s = msg.args[1]
            s = re.sub(self._googleRe, self._getColorGoogle, s)
            msg = ircmsgs.privmsg(msg.args[0], s, msg=msg)
        return msg

    _gsearchUrl = 'http://ajax.googleapis.com/ajax/services/search/web'
    @internationalizeDocstring
    def search(self, query, channel, options={}):
        """Perform a search using Google's AJAX API.
        search("search phrase", options={})

        Valid options are:
            smallsearch - True/False (Default: False)
            filter - {active,moderate,off} (Default: "moderate")
            language - Restrict search to documents in the given language
                       (Default: "lang_en")
        """
        ref = self.registryValue('referer')
        if not ref:
            ref = 'http://%s/%s' % (dynamic.irc.server,
                                    dynamic.irc.nick)
        headers = utils.web.defaultHeaders
        headers['Referer'] = ref
        opts = {'q': query, 'v': '1.0'}
        for (k, v) in options.iteritems():
            if k == 'smallsearch':
                if v:
                    opts['rsz'] = 'small'
                else:
                    opts['rsz'] = 'large'
            elif k == 'filter':
                opts['safe'] = v
            elif k == 'language':
                opts['lr'] = v
        defLang = self.registryValue('defaultLanguage', channel)
        if 'lr' not in opts and defLang:
            opts['lr'] = defLang
        if 'safe' not in opts:
            opts['safe'] = self.registryValue('searchFilter', dynamic.channel)
        if 'rsz' not in opts:
            opts['rsz'] = 'large'

        text = utils.web.getUrl('%s?%s' % (self._gsearchUrl,
                                           urllib.urlencode(opts)),
                                headers=headers).decode('utf8')
        data = json.loads(text)
        if data['responseStatus'] != 200:
            raise callbacks.Error, _('We broke The Google!')
        return data

    def formatData(self, data, bold=True, max=0, onetoone=False):
        if isinstance(data, basestring):
            return data
        results = []
        if max:
            data = data[:max]
        for result in data:
            title = utils.web.htmlToText(result['titleNoFormatting']\
                                         .encode('utf-8'))
            url = result['unescapedUrl']
            if sys.version_info[0] < 3:
                url = url.encode('utf-8')
            if title:
                if bold:
                    title = ircutils.bold(title)
                results.append(format('%s: %u', title, url))
            else:
                results.append(url)
        if sys.version_info[0] < 3:
            repl = lambda x:x if isinstance(x, unicode) else unicode(x, 'utf8')
            results = map(repl, results)
        if not results:
            return [_('No matches found.')]
        elif onetoone:
            return results
        else:
            return [u'; '.join(results)]

    @internationalizeDocstring
    def lucky(self, irc, msg, args, opts, text):
        """[--snippet] <search>

        Does a google search, but only returns the first result.
        If option --snippet is given, returns also the page text snippet.
        """
        opts = dict(opts)
        data = self.search(text, msg.args[0], {'smallsearch': True})
        if data['responseData']['results']:
            url = data['responseData']['results'][0]['unescapedUrl']
            if opts.has_key('snippet'):
                snippet = data['responseData']['results'][0]['content']
                snippet = " | " + utils.web.htmlToText(snippet, tagReplace='')
            else:
                snippet = ""
            result = url + snippet
            irc.reply(result)
        else:
            irc.reply(_('Google found nothing.'))
    lucky = wrap(lucky, [getopts({'snippet':'',}), 'text'])

    @internationalizeDocstring
    def google(self, irc, msg, args, optlist, text):
        """<search> [--{filter,language} <value>]

        Searches google.com for the given string.  As many results as can fit
        are included.  --language accepts a language abbreviation; --filter
        accepts a filtering level ('active', 'moderate', 'off').
        """
        if 'language' in optlist and optlist['language'].lower() not in \
           conf.supybot.plugins.Google.safesearch.validStrings:
            irc.errorInvalid('language')
        data = self.search(text, msg.args[0], dict(optlist))
        if data['responseStatus'] != 200:
            irc.reply(_('We broke The Google!'))
            return
        bold = self.registryValue('bold', msg.args[0])
        max = self.registryValue('maximumResults', msg.args[0])
        # We don't use supybot.reply.oneToOne here, because you generally
        # do not want @google to echo ~20 lines of results, even if you
        # have reply.oneToOne enabled.
        onetoone = self.registryValue('oneToOne', msg.args[0])
        for result in self.formatData(data['responseData']['results'],
                                  bold=bold, max=max, onetoone=onetoone):
            irc.reply(result)
    google = wrap(google, [getopts({'language':'something',
                                    'filter':''}),
                           'text'])

    @internationalizeDocstring
    def cache(self, irc, msg, args, url):
        """<url>

        Returns a link to the cached version of <url> if it is available.
        """
        data = self.search(url, msg.args[0], {'smallsearch': True})
        if data['responseData']['results']:
            m = data['responseData']['results'][0]
            if m['cacheUrl']:
                url = m['cacheUrl'].encode('utf-8')
                irc.reply(url)
                return
        irc.error(_('Google seems to have no cache for that site.'))
    cache = wrap(cache, ['url'])

    @internationalizeDocstring
    def fight(self, irc, msg, args):
        """<search string> <search string> [<search string> ...]

        Returns the results of each search, in order, from greatest number
        of results to least.
        """
        channel = msg.args[0]
        results = []
        for arg in args:
            data = self.search(arg, channel, {'smallsearch': True})
            count = data['responseData']['cursor'].get('estimatedResultCount',
                                                       0)
            results.append((int(count), arg))
        results.sort()
        results.reverse()
        if self.registryValue('bold', msg.args[0]):
            bold = ircutils.bold
        else:
            bold = repr
        s = ', '.join([format('%s: %i', bold(s), i) for (i, s) in results])
        irc.reply(s)

    @internationalizeDocstring
    def translate(self, irc, msg, args, sourceLang, targetLang, text):
        """<source language> [to] <target language> <text>

        Returns <text> translated from <source language> into <target
        language>.
        """

        channel = msg.args[0]

        headers = utils.web.defaultHeaders
        headers['User-Agent'] = ('Mozilla/5.0 (X11; U; Linux i686) '
                                 'Gecko/20071127 Firefox/2.0.0.11')

        sourceLang = urllib.quote(sourceLang)
        targetLang = urllib.quote(targetLang)

        text = urllib.quote(text)

        result = utils.web.getUrlFd('http://translate.google.com/translate_a/t'
                                    '?client=t&hl=en&sl=%s&tl=%s&multires=1'
                                    '&otf=1&ssel=0&tsel=0&uptl=en&sc=1&text='
                                    '%s' % (sourceLang, targetLang, text),
                                    headers).read().decode('utf8')

        while ',,' in result:
            result = result.replace(',,', ',null,')
        while '[,' in result:
            result = result.replace('[,', '[')
        data = json.loads(result)

        try:
            language = data[2]
        except:
            language = 'unknown'

        irc.reply(''.join(x[0] for x in data[0]), language)
    translate = wrap(translate, ['something', 'to', 'something', 'text'])

    def googleSnarfer(self, irc, msg, match):
        r"^google\s+(.*)$"
        if not self.registryValue('searchSnarfer', msg.args[0]):
            return
        searchString = match.group(1)
        data = self.search(searchString, msg.args[0], {'smallsearch': True})
        if data['responseData']['results']:
            url = data['responseData']['results'][0]['unescapedUrl']
            irc.reply(url, prefixNick=False)
    googleSnarfer = urlSnarfer(googleSnarfer)

    def _googleUrl(self, s, channel):
        s = urllib.quote_plus(s)
        url = r'http://%s/search?q=%s' % \
                (self.registryValue('baseUrl', channel), s)
        return url

    def _googleUrlIG(self, s, channel):
        s = urllib.quote_plus(s)
        url = r'http://%s/ig/calculator?hl=en&q=%s' % \
                (self.registryValue('baseUrl', channel), s)
        return url

    _calcRe1 = re.compile(r'
]*>(.*?)
', re.I)
    _calcRe2 = re.compile(r'<h\d class="?r"?[^>]*>(?:)?(.*?)(?:)?</h\d>', re.I | re.S)
    _calcSupRe = re.compile(r'(.*?)', re.I)
    _calcFontRe = re.compile(r'(.*?)')
    _calcTimesRe = re.compile(r'&(?:times|#215);')
    @internationalizeDocstring
    def calc(self, irc, msg, args, expr):
        """<expression>

        Uses Google's calculator to calculate the value of <expression>.
        """
        channel = msg.args[0]
        if not ircutils.isChannel(channel):
            channel = None
        urlig = self._googleUrlIG(expr, channel)
        js = utils.web.getUrl(urlig).decode('utf8')
        # Convert JavaScript to JSON. Ouch.
        js = js \
                .replace('lhs:','"lhs":') \
                .replace('rhs:','"rhs":') \
                .replace('error:','"error":') \
                .replace('icc:','"icc":') \
                .replace('\\', '\\\\')
        js = json.loads(js)

        url = self._googleUrl(expr, channel)
        html = utils.web.getUrl(url).decode('utf8')
        match = self._calcRe1.search(html)
        if match is None:
            match = self._calcRe2.search(html)
        if match is not None:
            s = match.group(1)
            s = self._calcSupRe.sub(r'^(\1)', s)
            s = self._calcFontRe.sub(r',', s)
            s = self._calcTimesRe.sub(r'*', s)
            s = utils.web.htmlToText(s)
            if ' = ' in s: # Extra check, since the regex seems to fail.
                irc.reply(s)
                return
            elif js['lhs'] and js['rhs']:
                # Outputs the original result. Might look ugly.
                irc.reply("%s = %s" % (js['lhs'], js['rhs'],))
                return
        irc.reply(_('Google says: Error: %s.') % (js['error'],))
        irc.reply('Google\'s calculator didn\'t come up with anything.')
    calc = wrap(calc, ['text'])

    _phoneRe = re.compile(r'Phonebook.*?(.*?)<a href')
    @internationalizeDocstring
    def phonebook(self, irc, msg, args, phonenumber):
        """<phone number>

        Looks <phone number> up on Google.
        """
        channel = msg.args[0]
        if not ircutils.isChannel(channel):
            channel = None
        url = self._googleUrl(phonenumber, channel)
        html = utils.web.getUrl(url).decode('utf8')
        m = self._phoneRe.search(html)
        if m is not None:
            s = m.group(1)
            s = s.replace('', '')
            s = s.replace('', '')
            s = utils.web.htmlToText(s)
            irc.reply(s)
        else:
            irc.reply(_('Google\'s phonebook didn\'t come up with anything.'))
    phonebook = wrap(phonebook, ['text'])

Class = Google

# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# Copyright (C) 2005-2009 Edgewall Software
# Copyright (C) 2005-2006 Christopher Lenz <cmlenz@gmx.de>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: Christopher Lenz <cmlenz@gmx.de>

from glob import glob
import imp
import os.path
import pkg_resources
from pkg_resources import working_set, DistributionNotFound, VersionConflict, \
                          UnknownExtra
import sys

from trac.util import get_doc, get_module_path, get_sources, get_pkginfo
from trac.util.text import exception_to_unicode, to_unicode

__all__ = ['load_components']

def _enable_plugin(env, module):
    """Enable the given plugin module if it wasn't disabled explicitly."""
    if env.is_component_enabled(module) is None:
        env.enable_component(module)

def load_eggs(entry_point_name):
    """Loader that loads any eggs on the search path and `sys.path`."""
    def _load_eggs(env, search_path, auto_enable=None):
        # Note that the following doesn't seem to support unicode search_path
        distributions, errors = working_set.find_plugins(
            pkg_resources.Environment(search_path)
        )
        for dist in distributions:
            if dist not in working_set:
                env.log.debug('Adding plugin %s from %s', dist, dist.location)
                working_set.add(dist)

        def _log_error(item, e):
            ue = exception_to_unicode(e)
            if isinstance(e, DistributionNotFound):
                env.log.debug('Skipping "%s": ("%s" not found)', item, ue)
            elif isinstance(e, VersionConflict):
                env.log.error('Skipping "%s": (version conflict "%s")',
                              item, ue)
            elif isinstance(e, UnknownExtra):
                env.log.error('Skipping "%s": (unknown extra "%s")', item, ue)
            elif isinstance(e, ImportError):
                env.log.error('Skipping "%s": (can\'t import "%s")', item, ue)
            else:
                env.log.error('Skipping "%s": %s)', item,
                              exception_to_unicode(e, traceback=True))

        for dist, e in errors.iteritems():
            _log_error(dist, e)

        for entry in sorted(working_set.iter_entry_points(entry_point_name),
                            key=lambda entry: entry.name):
            env.log.debug('Loading %s from %s', entry.name, entry.dist.location)
            try:
                entry.load(require=True)
            except Exception, e:
                _log_error(entry, e)
            else:
                if os.path.dirname(entry.dist.location) == auto_enable:
                    _enable_plugin(env, entry.module_name)
    return _load_eggs

def load_py_files():
    """Loader that look for Python source files in the plugins directories,
    which simply get imported, thereby registering them with the component
    manager if they define any components.
    """
    def _load_py_files(env, search_path, auto_enable=None):
        for path in search_path:
            plugin_files = glob(os.path.join(path, '*.py'))
            for plugin_file in plugin_files:
                try:
                    plugin_name = os.path.basename(plugin_file[:-3])
                    env.log.debug('Loading file plugin %s from %s' % \
                                  (plugin_name, plugin_file))
                    if plugin_name not in sys.modules:
                        module = imp.load_source(plugin_name, plugin_file)
                    if path == auto_enable:
                        _enable_plugin(env, plugin_name)
                except Exception, e:
                    env.log.error('Failed to load plugin from %s: %s',
                                  plugin_file,
                                  exception_to_unicode(e, traceback=True))

    return _load_py_files

def get_plugins_dir(env):
    """Return the path to the `plugins` directory of the environment."""
    plugins_dir = os.path.realpath(os.path.join(env.path, 'plugins'))
    return os.path.normcase(plugins_dir)

def load_components(env, extra_path=None, loaders=(load_eggs('trac.plugins'),
                                                   load_py_files())):
    """Load all plugin components found on the given search path."""
    plugins_dir = get_plugins_dir(env)
    search_path = [plugins_dir]
    if extra_path:
        search_path += list(extra_path)

    for loadfunc in loaders:
        loadfunc(env, search_path, auto_enable=plugins_dir)

def get_plugin_info(env, include_core=False):
    """Return package information about Trac core and installed plugins."""
    path_sources = {}

    def find_distribution(module):
        name = module.__name__
        path = get_module_path(module)
        sources = path_sources.get(path)
        if sources is None:
            sources = path_sources[path] = get_sources(path)
        dist = sources.get(name.replace('.', '/') + '.py')
        if dist is None:
            dist = sources.get(name.replace('.', '/') + '/__init__.py')
        if dist is None:
            # This is a plain Python source file, not an egg
            dist = pkg_resources.Distribution(project_name=name,
                                              version='',
                                              location=module.__file__)
        return dist

    plugins_dir = get_plugins_dir(env)
    plugins = {}
    from trac.core import ComponentMeta
    for component in ComponentMeta._components:
        module = sys.modules[component.__module__]

        dist = find_distribution(module)
        plugin_filename = None
        if os.path.realpath(os.path.dirname(dist.location)) == plugins_dir:
            plugin_filename = os.path.basename(dist.location)

        if dist.project_name not in plugins:
            readonly = True
            if plugin_filename and os.access(dist.location,
                                             os.F_OK + os.W_OK):
                readonly = False
            # retrieve plugin metadata
            info = get_pkginfo(dist)
            if not info:
                info = {}
                for k in ('author', 'author_email', 'home_page', 'url',
                          'license', 'trac'):
                    v = getattr(module, k, '')
                    if v and isinstance(v, basestring):
                        if k == 'home_page' or k == 'url':
                            k = 'home_page'
                            v = v.replace('$', '').replace('URL: ', '')
                        else:
                            v = to_unicode(v)
                        info[k] = v
            else:
                # Info found; set all those fields to "None" that have the
                # value "UNKNOWN" as this is the value for fields that
                # aren't specified in "setup.py"
                for k in info:
                    if info[k] == 'UNKNOWN':
                        info[k] = ''
                    else:
                        # Must be encoded as unicode as otherwise Genshi
                        # may raise a "UnicodeDecodeError".
                        info[k] = to_unicode(info[k])

            # retrieve plugin version info
            version = dist.version
            if not version:
                version = (getattr(module, 'version', '') or
                           getattr(module, 'revision', ''))
                # special handling for "$Rev$" strings
                version = version.replace('$', '').replace('Rev: ', 'r')
            plugins[dist.project_name] = {
                'name': dist.project_name, 'version': version,
                'path': dist.location, 'plugin_filename': plugin_filename,
                'readonly': readonly, 'info': info, 'modules': {},
            }
        modules = plugins[dist.project_name]['modules']
        if module.__name__ not in modules:
            summary, description = get_doc(module)
            plugins[dist.project_name]['modules'][module.__name__] = {
                'summary': summary, 'description': description,
                'components': {},
            }
        full_name = module.__name__ + '.' + component.__name__
        summary, description = get_doc(component)
        c = component
        if c in env and not issubclass(c, env.__class__):
            c = component(env)
        modules[module.__name__]['components'][component.__name__] = {
            'full_name': full_name,
            'summary': summary, 'description': description,
            'enabled': env.is_component_enabled(component),
            'required': getattr(c, 'required', False),
        }
    if not include_core:
        for name in plugins.keys():
            if name.lower() == 'trac':
                plugins.pop(name)
    return sorted(plugins.itervalues(),
                  key=lambda p: (p['name'].lower() != 'trac',
                                 p['name'].lower()))

def match_plugins_to_frames(plugins, frames):
    """Add a `frame_idx` element to plugin information as returned by
    `get_plugin_info()`, containing the index of the highest frame in the
    list that was located in the plugin.
    """
    egg_frames = [(i, f) for i, f in enumerate(frames)
                  if f['filename'].startswith('build/')]

    def find_egg_frame_index(plugin):
        for dist in pkg_resources.find_distributions(plugin['path'],
                                                     only=True):
            try:
                sources = dist.get_metadata('SOURCES.txt')
                for src in sources.splitlines():
                    if src.endswith('.py'):
                        nsrc = src.replace('\\', '/')
                        for i, f in egg_frames:
                            if f['filename'].endswith(nsrc):
                                plugin['frame_idx'] = i
                                return
            except KeyError:
                pass    # Metadata not found

    for plugin in plugins:
        base, ext = os.path.splitext(plugin['path'])
        if ext == '.egg' and egg_frames:
            find_egg_frame_index(plugin)
        else:
            for i, f in enumerate(frames):
                if f['filename'].startswith(base):
                    plugin['frame_idx'] = i
                    break
<commit_msg>0.12.2dev: Log a full traceback on `ImportError` when loading modules.<commit_after># -*- coding: utf-8 -*-
#
# Copyright (C) 2005-2009 Edgewall Software
# Copyright (C) 2005-2006 Christopher Lenz <cmlenz@gmx.de>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: Christopher Lenz <cmlenz@gmx.de>

from glob import glob
import imp
import os.path
import pkg_resources
from pkg_resources import working_set, DistributionNotFound, VersionConflict, \
                          UnknownExtra
import sys

from trac.util import get_doc, get_module_path, get_sources, get_pkginfo
from trac.util.text import exception_to_unicode, to_unicode

__all__ = ['load_components']

def _enable_plugin(env, module):
    """Enable the given plugin module if it wasn't disabled explicitly."""
    if env.is_component_enabled(module) is None:
        env.enable_component(module)

def load_eggs(entry_point_name):
    """Loader that loads any eggs on the search path and `sys.path`."""
    def _load_eggs(env, search_path, auto_enable=None):
        # Note that the following doesn't seem to support unicode search_path
        distributions, errors = working_set.find_plugins(
            pkg_resources.Environment(search_path)
        )
        for dist in distributions:
            if dist not in working_set:
                env.log.debug('Adding plugin %s from %s', dist, dist.location)
                working_set.add(dist)

        def _log_error(item, e):
            ue = exception_to_unicode(e)
            if isinstance(e, DistributionNotFound):
                env.log.debug('Skipping "%s": ("%s" not found)', item, ue)
            elif isinstance(e, VersionConflict):
                env.log.error('Skipping "%s": (version conflict "%s")',
                              item, ue)
            elif isinstance(e, UnknownExtra):
                env.log.error('Skipping "%s": (unknown extra "%s")', item, ue)
            else:
                env.log.error('Skipping "%s": %s', item,
                              exception_to_unicode(e, traceback=True))

        for dist, e in errors.iteritems():
            _log_error(dist, e)

        for entry in sorted(working_set.iter_entry_points(entry_point_name),
                            key=lambda entry: entry.name):
            env.log.debug('Loading %s from %s', entry.name, entry.dist.location)
            try:
                entry.load(require=True)
            except Exception, e:
                _log_error(entry, e)
            else:
                if os.path.dirname(entry.dist.location) == auto_enable:
                    _enable_plugin(env, entry.module_name)
    return _load_eggs

def load_py_files():
    """Loader that look for Python source files in the plugins directories,
    which simply get imported, thereby registering them with the component
    manager if they define any components.
    """
    def _load_py_files(env, search_path, auto_enable=None):
        for path in search_path:
            plugin_files = glob(os.path.join(path, '*.py'))
            for plugin_file in plugin_files:
                try:
                    plugin_name = os.path.basename(plugin_file[:-3])
                    env.log.debug('Loading file plugin %s from %s' % \
                                  (plugin_name, plugin_file))
                    if plugin_name not in sys.modules:
                        module = imp.load_source(plugin_name, plugin_file)
                    if path == auto_enable:
                        _enable_plugin(env, plugin_name)
                except Exception, e:
                    env.log.error('Failed to load plugin from %s: %s',
                                  plugin_file,
                                  exception_to_unicode(e, traceback=True))

    return _load_py_files

def get_plugins_dir(env):
    """Return the path to the `plugins` directory of the environment."""
    plugins_dir = os.path.realpath(os.path.join(env.path, 'plugins'))
    return os.path.normcase(plugins_dir)

def load_components(env, extra_path=None, loaders=(load_eggs('trac.plugins'),
                                                   load_py_files())):
    """Load all plugin components found on the given search path."""
    plugins_dir = get_plugins_dir(env)
    search_path = [plugins_dir]
    if extra_path:
        search_path += list(extra_path)

    for loadfunc in loaders:
        loadfunc(env, search_path, auto_enable=plugins_dir)

def get_plugin_info(env, include_core=False):
    """Return package information about Trac core and installed plugins."""
    path_sources = {}

    def find_distribution(module):
        name = module.__name__
        path = get_module_path(module)
        sources = path_sources.get(path)
        if sources is None:
            sources = path_sources[path] = get_sources(path)
        dist = sources.get(name.replace('.', '/') + '.py')
        if dist is None:
            dist = sources.get(name.replace('.', '/') + '/__init__.py')
        if dist is None:
            # This is a plain Python source file, not an egg
            dist = pkg_resources.Distribution(project_name=name,
                                              version='',
                                              location=module.__file__)
        return dist

    plugins_dir = get_plugins_dir(env)
    plugins = {}
    from trac.core import ComponentMeta
    for component in ComponentMeta._components:
        module = sys.modules[component.__module__]

        dist = find_distribution(module)
        plugin_filename = None
        if os.path.realpath(os.path.dirname(dist.location)) == plugins_dir:
            plugin_filename = os.path.basename(dist.location)

        if dist.project_name not in plugins:
            readonly = True
            if plugin_filename and os.access(dist.location,
                                             os.F_OK + os.W_OK):
                readonly = False
            # retrieve plugin metadata
            info = get_pkginfo(dist)
            if not info:
                info = {}
                for k in ('author', 'author_email', 'home_page', 'url',
                          'license', 'trac'):
                    v = getattr(module, k, '')
                    if v and isinstance(v, basestring):
                        if k == 'home_page' or k == 'url':
                            k = 'home_page'
                            v = v.replace('$', '').replace('URL: ', '')
                        else:
                            v = to_unicode(v)
                        info[k] = v
            else:
                # Info found; set all those fields to "None" that have the
                # value "UNKNOWN" as this is the value for fields that
                # aren't specified in "setup.py"
                for k in info:
                    if info[k] == 'UNKNOWN':
                        info[k] = ''
                    else:
                        # Must be encoded as unicode as otherwise Genshi
                        # may raise a "UnicodeDecodeError".
                        info[k] = to_unicode(info[k])

            # retrieve plugin version info
            version = dist.version
            if not version:
                version = (getattr(module, 'version', '') or
                           getattr(module, 'revision', ''))
                # special handling for "$Rev$" strings
                version = version.replace('$', '').replace('Rev: ', 'r')
            plugins[dist.project_name] = {
                'name': dist.project_name, 'version': version,
                'path': dist.location, 'plugin_filename': plugin_filename,
                'readonly': readonly, 'info': info, 'modules': {},
            }
        modules = plugins[dist.project_name]['modules']
        if module.__name__ not in modules:
            summary, description = get_doc(module)
            plugins[dist.project_name]['modules'][module.__name__] = {
                'summary': summary, 'description': description,
                'components': {},
            }
        full_name = module.__name__ + '.' + component.__name__
        summary, description = get_doc(component)
        c = component
        if c in env and not issubclass(c, env.__class__):
            c = component(env)
        modules[module.__name__]['components'][component.__name__] = {
            'full_name': full_name,
            'summary': summary, 'description': description,
            'enabled': env.is_component_enabled(component),
            'required': getattr(c, 'required', False),
        }
    if not include_core:
        for name in plugins.keys():
            if name.lower() == 'trac':
                plugins.pop(name)
    return sorted(plugins.itervalues(),
                  key=lambda p: (p['name'].lower() != 'trac',
                                 p['name'].lower()))

def match_plugins_to_frames(plugins, frames):
    """Add a `frame_idx` element to plugin information as returned by
    `get_plugin_info()`, containing the index of the highest frame in the
    list that was located in the plugin.
    """
    egg_frames = [(i, f) for i, f in enumerate(frames)
                  if f['filename'].startswith('build/')]

    def find_egg_frame_index(plugin):
        for dist in pkg_resources.find_distributions(plugin['path'],
                                                     only=True):
            try:
                sources = dist.get_metadata('SOURCES.txt')
                for src in sources.splitlines():
                    if src.endswith('.py'):
                        nsrc = src.replace('\\', '/')
                        for i, f in egg_frames:
                            if f['filename'].endswith(nsrc):
                                plugin['frame_idx'] = i
                                return
            except KeyError:
                pass    # Metadata not found

    for plugin in plugins:
        base, ext = os.path.splitext(plugin['path'])
        if ext == '.egg' and egg_frames:
            find_egg_frame_index(plugin)
        else:
            for i, f in enumerate(frames):
                if f['filename'].startswith(base):
                    plugin['frame_idx'] = i
                    break
<|endoftext|>


<commit_before>import binascii
from direct_connect import qaes
from django.db import models
from django.contrib.auth.models import User
from mmcd.custom.base.models import Customer, Transaction
from django.conf import settings

'''
See http://www.tylerlesmann.com/2008/dec/19/encrypting-database-data-django/
Written by Tyler Lesmann
and
http://github.com/django-extensions/django-extensions/blob/master/django_extensions/db/fields/encrypted.py
The following is a hybrid of the two approaches because Lesmann's wasn't encrypting when the field value was set manually
instead of directly from a form
'''

class BaseEncryptedCharField(models.CharField):
	prefix = 'enc_str:::'
	def to_python(self, value):
		if value:
			if (value.startswith(self.prefix)):
				return qaes.decrypt(settings.AESKEY, binascii.a2b_base64(value[len(self.prefix):]))
			else:
				return value
		else:
			return ""
	def get_db_prep_value(self, value):
		if value:
			if not value.startswith(self.prefix):
				return self.prefix + binascii.b2a_base64(qaes.encrypt(settings.AESKEY, value))
			else:
				return value
		else:
			return ""

class EncryptedCharField(BaseEncryptedCharField):
	__metaclass__ = models.SubfieldBase

class Address:
	def __init__(self):
		self.delimiter = hex(31)
	def initialize(self, firstname, lastname, street, city, state, zipcode, country, countrycode, email_address=None, street2=None):
		self.firstname = firstname
		self.lastname = lastname
		self.street = street
		self.street2 = street2 or ""
		self.city = city
		self.state = state
		self.zipcode = zipcode
		self.country = country
		self.countrycode = countrycode
		self.email_address = email_address or ""
	def getAddressString(self):
		return self.delimiter.join((self.firstname, self.lastname, self.street, self.city,
			self.state, self.zipcode, self.country, self.countrycode, self.email_address, self.street2,))
	def parseDecrypted(self, inStr):
		s = inStr.split(self.delimiter)
		self.initialize(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9])

class CreditCardInfo:
	def __init__(self, acct, expdate, cvv2, cctype):
		self.acct = acct
		self.expdate = expdate.strftime("%m%y")
		self.cvv2 = cvv2
		self.cctype = cctype

class Information(models.Model):
	address = EncryptedCharField(max_length=4000)
	transaction = models.ForeignKey(Transaction)
	user = models.ForeignKey(User, blank=True, null=True)
	customer = models.ForeignKey(Customer)
	created_at = models.DateTimeField(auto_now_add=True)
	updated_at = models.DateTimeField(auto_now=True)

class ServerResponse(models.Model):
	result = models.CharField(max_length=5)
	respMsg = models.CharField(max_length = 250)
	message = models.CharField(max_length = 250)
	authCode = models.CharField(max_length = 25, null = True)
	pnref = EncryptedCharField(max_length=10, null = True)
	hostCode = EncryptedCharField(max_length=10, null = True)
	getOrigResult = models.CharField(max_length=25, null = True)
	extData = models.CharField(max_length = 2000)
	ipAddress = models.IPAddressField()
	info = models.ForeignKey(Information, null=True)

<commit_msg>Change to external project on which this unfortunately still depends<commit_after>import binascii
from direct_connect import qaes
from django.db import models
from django.contrib.auth.models import User
#TODO - REFACTOR
from mmcd.custom.product.models import Customer, Transaction
from django.conf import settings

'''
See http://www.tylerlesmann.com/2008/dec/19/encrypting-database-data-django/
Written by Tyler Lesmann
and
http://github.com/django-extensions/django-extensions/blob/master/django_extensions/db/fields/encrypted.py
The following is a hybrid of the two approaches because Lesmann's wasn't encrypting when the field value was set manually
instead of directly from a form
'''

class BaseEncryptedCharField(models.CharField):
	prefix = 'enc_str:::'
	def to_python(self, value):
		if value:
			if (value.startswith(self.prefix)):
				return qaes.decrypt(settings.AESKEY, binascii.a2b_base64(value[len(self.prefix):]))
			else:
				return value
		else:
			return ""
	def get_db_prep_value(self, value):
		if value:
			if not value.startswith(self.prefix):
				return self.prefix + binascii.b2a_base64(qaes.encrypt(settings.AESKEY, value))
			else:
				return value
		else:
			return ""

class EncryptedCharField(BaseEncryptedCharField):
	__metaclass__ = models.SubfieldBase

class Address:
	def __init__(self):
		self.delimiter = hex(31)
	def initialize(self, firstname, lastname, street, city, state, zipcode, country, countrycode, email_address=None, street2=None):
		self.firstname = firstname
		self.lastname = lastname
		self.street = street
		self.street2 = street2 or ""
		self.city = city
		self.state = state
		self.zipcode = zipcode
		self.country = country
		self.countrycode = countrycode
		self.email_address = email_address or ""
	def getAddressString(self):
		return self.delimiter.join((self.firstname, self.lastname, self.street, self.city,
			self.state, self.zipcode, self.country, self.countrycode, self.email_address, self.street2,))
	def parseDecrypted(self, inStr):
		s = inStr.split(self.delimiter)
		self.initialize(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9])

class CreditCardInfo:
	def __init__(self, acct, expdate, cvv2, cctype):
		self.acct = acct
		self.expdate = expdate.strftime("%m%y")
		self.cvv2 = cvv2
		self.cctype = cctype

class Information(models.Model):
	address = EncryptedCharField(max_length=4000)
	transaction = models.ForeignKey(Transaction)
	user = models.ForeignKey(User, blank=True, null=True)
	customer = models.ForeignKey(Customer)
	created_at = models.DateTimeField(auto_now_add=True)
	updated_at = models.DateTimeField(auto_now=True)

class ServerResponse(models.Model):
	result = models.CharField(max_length=5)
	respMsg = models.CharField(max_length = 250)
	message = models.CharField(max_length = 250)
	authCode = models.CharField(max_length = 25, null = True)
	pnref = EncryptedCharField(max_length=10, null = True)
	hostCode = EncryptedCharField(max_length=10, null = True)
	getOrigResult = models.CharField(max_length=25, null = True)
	extData = models.CharField(max_length = 2000)
	ipAddress = models.IPAddressField()
	info = models.ForeignKey(Information, null=True)

<|endoftext|>


<commit_before><commit_msg>Fixing something<commit_after><|endoftext|>


<commit_before>###
# Copyright (c) 2005, Daniel DiPaolo
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions, and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions, and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   * Neither the name of the author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###

import re

from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils

class Praise(plugins.ChannelIdDatabasePlugin):
    """Praise is a plugin for ... well, praising things.  Feel free to add
    your own flavor to it by customizing what praises it gives.  Use "praise
    add <text>" to add new ones, making sure to include "$who" in <text> where
    you want to insert the thing being praised.
    """
    _meRe = re.compile(r'\bme\b', re.I)
    _myRe = re.compile(r'\bmy\b', re.I)
    def _replaceFirstPerson(self, s, nick):
        s = self._meRe.sub(nick, s)
        s = self._myRe.sub('%s\'s' % nick, s)
        return s

    def addValidator(self, irc, text):
        if '$who' not in text:
            irc.error('Praises must contain $who.', Raise=True)

    def praise(self, irc, msg, args, channel, id, text):
        """[<channel>] [<id>] <who|what> [for <reason>]

        Praises <who|what> (for <reason>, if given).  If <id> is given, uses
        that specific praise.  <channel> is only necessary if the message isn't
        sent in the channel itself.
        """
        if ' for ' in text:
            (target, reason) = map(str.strip, text.split(' for ', 1))
        else:
            (target, reason) = (text, '')
        if ircutils.strEqual(target, irc.nick):
            target = 'itself'
        if id is not None:
            try:
                praise = self.db.get(channel, id)
            except KeyError:
                irc.error(format('There is no praise with id #%i.', id))
                return
        else:
            praise = self.db.random(channel)
            if not praise:
                irc.error(format('There are no praises in my database ' \
                                 'for %s.', channel))
                return
        text = self._replaceFirstPerson(praise.text, msg.nick)
        reason = self._replaceFirstPerson(reason, msg.nick)
        if target.endswith('.'):
            target = target.rstrip('.')
        target = self._replaceFirstPerson(target, msg.nick)
        text = text.replace('$who', target)
        if reason:
            text += ' for ' + reason
        if self.registryValue('showIds', channel):
            text += format(' (#%i)', praise.id)
        irc.reply(text, action=True)
    praise = wrap(praise, ['channeldb', optional('id'), 'text'])

Class = Praise

# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
<commit_msg>plugin/Praise: Don't rstrip '.'. People can learn not to end their praise with a '.'.<commit_after>###
# Copyright (c) 2005, Daniel DiPaolo
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions, and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions, and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   * Neither the name of the author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###

import re

from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils

class Praise(plugins.ChannelIdDatabasePlugin):
    """Praise is a plugin for ... well, praising things.  Feel free to add
    your own flavor to it by customizing what praises it gives.  Use "praise
    add <text>" to add new ones, making sure to include "$who" in <text> where
    you want to insert the thing being praised.
    """
    _meRe = re.compile(r'\bme\b', re.I)
    _myRe = re.compile(r'\bmy\b', re.I)
    def _replaceFirstPerson(self, s, nick):
        s = self._meRe.sub(nick, s)
        s = self._myRe.sub('%s\'s' % nick, s)
        return s

    def addValidator(self, irc, text):
        if '$who' not in text:
            irc.error('Praises must contain $who.', Raise=True)

    def praise(self, irc, msg, args, channel, id, text):
        """[<channel>] [<id>] <who|what> [for <reason>]

        Praises <who|what> (for <reason>, if given).  If <id> is given, uses
        that specific praise.  <channel> is only necessary if the message isn't
        sent in the channel itself.
        """
        if ' for ' in text:
            (target, reason) = map(str.strip, text.split(' for ', 1))
        else:
            (target, reason) = (text, '')
        if ircutils.strEqual(target, irc.nick):
            target = 'itself'
        if id is not None:
            try:
                praise = self.db.get(channel, id)
            except KeyError:
                irc.error(format('There is no praise with id #%i.', id))
                return
        else:
            praise = self.db.random(channel)
            if not praise:
                irc.error(format('There are no praises in my database ' \
                                 'for %s.', channel))
                return
        text = self._replaceFirstPerson(praise.text, msg.nick)
        reason = self._replaceFirstPerson(reason, msg.nick)
        target = self._replaceFirstPerson(target, msg.nick)
        text = text.replace('$who', target)
        if reason:
            text += ' for ' + reason
        if self.registryValue('showIds', channel):
            text += format(' (#%i)', praise.id)
        irc.reply(text, action=True)
    praise = wrap(praise, ['channeldb', optional('id'), 'text'])

Class = Praise

# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
<|endoftext|>


<commit_before>"""
.. module:: weather
   :platform: Unix
   :synopsis: Contains utilities for obtaining weather data.

.. moduleauthor:: Phil Ngo <ngo.phil@gmail.com>
.. moduleauthor:: Miguel Perez <miguel@invalid.com>
.. moduleauthor:: Stephen Suffian <steve@invalid.com>
.. moduleauthor:: Sabina Tomkins <sabina@invalid.com>

"""

def get_lat_lng_from_zip_code(zip_code,google_apis_key):
    '''
    Return a lat and long given a zip code. (Centroid, see google apis
    documentation)
    '''
    zip_code=zip_code.replace(' ','+')
    zip_code=zip_code.replace(',','%2C')
    f = urllib2.urlopen('https://maps.googleapis.com/maps/api/geocode/json?address='+zip_code+'&key=' + google_apis_key)
    json_string = f.read()
    parsed_json_lat_lng = json.loads(json_string)
    lat=parsed_json_lat_lng['results'][0]['geometry']['location']['lat']
    lng=parsed_json_lat_lng['results'][0]['geometry']['location']['lng']
    return lat,lng
<commit_msg>update imports for weather module<commit_after>"""
.. module:: weather
   :platform: Unix
   :synopsis: Contains utilities for obtaining weather data.

.. moduleauthor:: Phil Ngo <ngo.phil@gmail.com>
.. moduleauthor:: Miguel Perez <miguel@invalid.com>
.. moduleauthor:: Stephen Suffian <steve@invalid.com>
.. moduleauthor:: Sabina Tomkins <sabina@invalid.com>

"""
import urllib2
import json

def get_lat_lng_from_zip_code(zip_code,google_apis_key):
    '''
    Return a lat and long given a zip code. (Centroid, see google apis
    documentation)
    '''
    zip_code=zip_code.replace(' ','+')
    zip_code=zip_code.replace(',','%2C')
    f = urllib2.urlopen('https://maps.googleapis.com/maps/api/geocode/json?address='+zip_code+'&key=' + google_apis_key)
    json_string = f.read()
    parsed_json_lat_lng = json.loads(json_string)
    lat=parsed_json_lat_lng['results'][0]['geometry']['location']['lat']
    lng=parsed_json_lat_lng['results'][0]['geometry']['location']['lng']
    return lat,lng
<|endoftext|>


<commit_before>#!/usr/bin/env python3

from __future__ import print_function
import numpy as np
# np.random.seed(1337)  # for reproducibility
# SEED = 1337

import os
import pickle
from time import localtime, strftime
from optparse import OptionParser

import keras
from keras.datasets import mnist
from keras.utils import np_utils
from keras import backend as K
from keras.optimizers import SGD

from bird.models.cuberun import CubeRun
from bird.models.resnet import ResNetBuilder
from bird.generators.sound import SoundDataGenerator

# Setup Callbacks for History
class HistoryCollector(keras.callbacks.Callback):
    def __init__(self, name):
        self.name = name
        self.data = []

    def on_epoch_end(self, batch, logs={}):
        self.data.append(logs.get(self.name))

def train_model(model_name, audio_mode, train_path, valid_path, batch_size, nb_classes, nb_epoch,
                nb_val_samples, samples_per_epoch, input_shape,
                weight_file_path, history_file_path, first_epoch, lock_file):

    img_rows, img_cols, nb_channels = input_shape

    model = None
    if model_name == 'cuberun':
	    model = CubeRun(nb_classes, input_shape)
    elif model_name == 'resnet_18':
            model = ResNetBuilder.build_resnet_18(input_shape, nb_classes)
    else:
        raise ValueError("Can not find model ", model_name, ".")

    sgd = SGD(lr=0.001, decay=1e-6, momentum=0.9, nesterov=True)
    model.compile(loss='categorical_crossentropy',
                  optimizer=sgd,
                  metrics=['accuracy'])

    if first_epoch=='False':
        # load weights
        model.load_weights(weight_file_path)
        print("loading weigths from: " + weight_file_path)
    else:
        print("using initial weights")

    # Callback history collectors
    trainLossHistory = HistoryCollector('loss')
    validLossHistory = HistoryCollector('val_loss')
    trainAccHistory = HistoryCollector('acc')
    validAccHistory = HistoryCollector('val_acc')
    checkpoint = keras.callbacks.ModelCheckpoint(weight_file_path,
                                                monitor='val_acc', verbose=0,
                                                save_best_only=True,
                                                save_weights_only=True,
                                                mode='auto')

    # train data generator
    train_datagen = SoundDataGenerator(
        rescale=1./255,
        time_shift=False,
        pitch_shift=False,
        augment_with_same_class=False,
        augment_with_noise=False)

    # validation data generator
    valid_datagen = SoundDataGenerator(
        rescale=1./255)

    # Generator for training data
    print("Loading training data...")
    train_generator = train_datagen.flow_from_directory(
        train_path,
        noise_path,
        target_size=(img_rows, img_cols),
        batch_size=batch_size,
        class_mode='categorical',
        audio_mode=audio_mode
        #save_to_dir='./visuals/augmented_samples'
        )

    # Generator for validation data
    print("Loading validation data...")
    valid_generator = valid_datagen.flow_from_directory(
        valid_path,
        noise_path,
        target_size=(img_rows, img_cols),
        batch_size=batch_size,
        class_mode='categorical',
        audio_mode=audio_mode
        #save_to_dir='./visuals/validation_samples',
        )

    # Fit model to generated training data
    model.fit_generator(
        train_generator,
        samples_per_epoch=samples_per_epoch,
        nb_epoch=nb_epoch,
        validation_data=valid_generator,
        nb_val_samples=nb_val_samples,
        callbacks=[trainLossHistory, validLossHistory, trainAccHistory,
                   validAccHistory, checkpoint])

    # save the weights
    model.save_weights(weight_file_path)
    print (strftime("%a, %d %b %Y %H:%M:%S +0000", localtime()))
    print ("The weights have been saved in: " + weight_file_path)

    # save history to file
    with open(history_file_path, 'wb') as output:
        pickle.dump(trainLossHistory.data, output, pickle.HIGHEST_PROTOCOL)
        pickle.dump(validLossHistory.data, output, pickle.HIGHEST_PROTOCOL)
        pickle.dump(trainAccHistory.data, output, pickle.HIGHEST_PROTOCOL)
        pickle.dump(validAccHistory.data, output, pickle.HIGHEST_PROTOCOL)
    print ("The history has been saved in: " + history_file_path)

    os.remove(lock_file)

    return weight_file_path

parser = OptionParser()
parser.add_option("--train_path", dest="train_path")
parser.add_option("--valid_path", dest="valid_path")
parser.add_option("--noise_path", dest="noise_path")
parser.add_option("--weight_path", dest="weight_path")
parser.add_option("--history_path", dest="history_path")
parser.add_option("--first_epoch", dest="first_epoch")
parser.add_option("--lock_file", dest="lock_file")
parser.add_option("--model_name", dest="model_name")

(options, args) = parser.parse_args()

print("Options:", options)

batch_size = 16
nb_classes = 20
nb_epoch   = 6
nb_val_samples = 613
samples_per_epoch = 2213
input_shape = (256, 256, 1)
audio_mode = 'mfcc'

noise_path = options.noise_path
train_path = options.train_path
valid_path = options.valid_path
history_file_path = options.history_path
weight_file_path = options.weight_path
first_epoch = options.first_epoch
lock_file = options.lock_file
model_name = options.model_name

train_model(model_name, audio_mode, train_path, valid_path, batch_size, nb_classes, nb_epoch,
            nb_val_samples, samples_per_epoch, input_shape, weight_file_path,
            history_file_path, first_epoch, lock_file)
<commit_msg>Add resnet 34/50/101/152<commit_after>#!/usr/bin/env python3

from __future__ import print_function
import numpy as np
# np.random.seed(1337)  # for reproducibility
# SEED = 1337

import os
import pickle
from time import localtime, strftime
from optparse import OptionParser

import keras
from keras.datasets import mnist
from keras.utils import np_utils
from keras import backend as K
from keras.optimizers import SGD

from bird.models.cuberun import CubeRun
from bird.models.resnet import ResNetBuilder
from bird.generators.sound import SoundDataGenerator

# Setup Callbacks for History
class HistoryCollector(keras.callbacks.Callback):
    def __init__(self, name):
        self.name = name
        self.data = []

    def on_epoch_end(self, batch, logs={}):
        self.data.append(logs.get(self.name))

def train_model(model_name, audio_mode, train_path, valid_path, batch_size, nb_classes, nb_epoch,
                nb_val_samples, samples_per_epoch, input_shape,
                weight_file_path, history_file_path, first_epoch, lock_file):

    img_rows, img_cols, nb_channels = input_shape

    model = None
    if model_name == 'cuberun':
	    model = CubeRun(nb_classes, input_shape)
    elif model_name == 'resnet_18':
            model = ResNetBuilder.build_resnet_18(input_shape, nb_classes)
    elif model_name == 'resnet_34':
            model = ResNetBuilder.build_resnet_34(input_shape, nb_classes)
    elif model_name == 'resnet_50':
            model = ResNetBuilder.build_resnet_50(input_shape, nb_classes)
    elif model_name == 'resnet_101':
            model = ResNetBuilder.build_resnet_101(input_shape, nb_classes)
    elif model_name == 'resnet_152':
            model = ResNetBuilder.build_resnet_152(input_shape, nb_classes)
    else:
        raise ValueError("Can not find model ", model_name, ".")

    sgd = SGD(lr=0.001, decay=1e-6, momentum=0.9, nesterov=True)
    model.compile(loss='categorical_crossentropy',
                  optimizer=sgd,
                  metrics=['accuracy'])

    if first_epoch=='False':
        # load weights
        model.load_weights(weight_file_path)
        print("loading weigths from: " + weight_file_path)
    else:
        print("using initial weights")

    # Callback history collectors
    trainLossHistory = HistoryCollector('loss')
    validLossHistory = HistoryCollector('val_loss')
    trainAccHistory = HistoryCollector('acc')
    validAccHistory = HistoryCollector('val_acc')
    checkpoint = keras.callbacks.ModelCheckpoint(weight_file_path,
                                                monitor='val_acc', verbose=0,
                                                save_best_only=True,
                                                save_weights_only=True,
                                                mode='auto')

    # train data generator
    train_datagen = SoundDataGenerator(
        rescale=1./255,
        time_shift=True,
        pitch_shift=True,
        augment_with_same_class=True,
        augment_with_noise=True)

    # validation data generator
    valid_datagen = SoundDataGenerator(
        rescale=1./255)

    # Generator for training data
    print("Loading training data...")
    train_generator = train_datagen.flow_from_directory(
        train_path,
        noise_path,
        target_size=(img_rows, img_cols),
        batch_size=batch_size,
        class_mode='categorical',
        audio_mode=audio_mode
        #save_to_dir='./visuals/augmented_samples'
        )

    # Generator for validation data
    print("Loading validation data...")
    valid_generator = valid_datagen.flow_from_directory(
        valid_path,
        noise_path,
        target_size=(img_rows, img_cols),
        batch_size=batch_size,
        class_mode='categorical',
        audio_mode=audio_mode
        #save_to_dir='./visuals/validation_samples',
        )

    # Fit model to generated training data
    model.fit_generator(
        train_generator,
        samples_per_epoch=samples_per_epoch,
        nb_epoch=nb_epoch,
        validation_data=valid_generator,
        nb_val_samples=nb_val_samples,
        callbacks=[trainLossHistory, validLossHistory, trainAccHistory,
                   validAccHistory, checkpoint])

    # save the weights
    model.save_weights(weight_file_path)
    print (strftime("%a, %d %b %Y %H:%M:%S +0000", localtime()))
    print ("The weights have been saved in: " + weight_file_path)

    # save history to file
    with open(history_file_path, 'wb') as output:
        pickle.dump(trainLossHistory.data, output, pickle.HIGHEST_PROTOCOL)
        pickle.dump(validLossHistory.data, output, pickle.HIGHEST_PROTOCOL)
        pickle.dump(trainAccHistory.data, output, pickle.HIGHEST_PROTOCOL)
        pickle.dump(validAccHistory.data, output, pickle.HIGHEST_PROTOCOL)
    print ("The history has been saved in: " + history_file_path)

    os.remove(lock_file)

    return weight_file_path

parser = OptionParser()
parser.add_option("--train_path", dest="train_path")
parser.add_option("--valid_path", dest="valid_path")
parser.add_option("--noise_path", dest="noise_path")
parser.add_option("--weight_path", dest="weight_path")
parser.add_option("--history_path", dest="history_path")
parser.add_option("--first_epoch", dest="first_epoch")
parser.add_option("--lock_file", dest="lock_file")
parser.add_option("--model_name", dest="model_name")

(options, args) = parser.parse_args()

print("Options:", options)

batch_size = 16
nb_classes = 20
nb_epoch   = 6
nb_val_samples = 309
samples_per_epoch = 2417
input_shape = (256, 512, 1)
audio_mode = 'spectrogram'

noise_path = options.noise_path
train_path = options.train_path
valid_path = options.valid_path
history_file_path = options.history_path
weight_file_path = options.weight_path
first_epoch = options.first_epoch
lock_file = options.lock_file
model_name = options.model_name

train_model(model_name, audio_mode, train_path, valid_path, batch_size, nb_classes, nb_epoch,
            nb_val_samples, samples_per_epoch, input_shape, weight_file_path,
            history_file_path, first_epoch, lock_file)
<|endoftext|>


<commit_before>"""Clowder repo management"""
import os
import subprocess
import sys
from termcolor import colored
from clowder.utility.git_utilities import (
    git_add,
    git_branches,
    git_checkout,
    git_commit,
    git_create_repo,
    git_fetch,
    git_is_dirty,
    git_pull,
    git_push,
    git_reset_head,
    git_status
)
from clowder.utility.clowder_utilities import (
    force_symlink,
    format_project_string,
    format_ref_string,
    print_validation,
    remove_prefix,
    validate_repo_state
)

class ClowderRepo(object):
    """Class encapsulating clowder repo information"""
    def __init__(self, root_directory):
        self.root_directory = root_directory
        self.clowder_path = os.path.join(self.root_directory, '.clowder')

    def add(self, files):
        """Add files in clowder repo to git index"""
        git_add(self.clowder_path, files)
        git_status(self.clowder_path)

    def branches(self):
        """Return current local branches"""
        return git_branches(self.clowder_path)

    def checkout(self, ref):
        """Checkout ref in clowder repo"""
        if self.is_dirty():
            print(' - Dirty repo. Please stash, commit, or discard your changes')
        else:
            git_checkout(self.clowder_path, ref)

    def clean(self):
        """Discard changes in clowder repo"""
        if self.is_dirty():
            print(' - Discard current changes')
            git_reset_head(self.clowder_path)
        else:
            print(' - No changes to discard')

    def commit(self, message):
        """Commit current changes in clowder repo"""
        git_commit(self.clowder_path, message)

    def init(self, url, branch):
        """Clone clowder repo from url"""
        repo_branch = 'refs/heads/' + branch
        git_create_repo(url, self.clowder_path, 'origin', repo_branch)
        self.link()

    def is_dirty(self):
        """Check if project is dirty"""
        return git_is_dirty(self.clowder_path)

    def link(self, version=None):
        """Create symlink pointing to clowder.yaml file"""
        if version is None:
            yaml_file = os.path.join(self.root_directory, '.clowder', 'clowder.yaml')
            path_output = colored('.clowder/clowder.yaml', 'cyan')
        else:
            relative_path = os.path.join('.clowder', 'versions', version, 'clowder.yaml')
            path_output = colored(relative_path, 'cyan')
            yaml_file = os.path.join(self.root_directory, relative_path)

        if os.path.isfile(yaml_file):
            yaml_symlink = os.path.join(self.root_directory, 'clowder.yaml')
            print(' - Symlink ' + path_output)
            force_symlink(yaml_file, yaml_symlink)
        else:
            print(path_output + " doesn't seem to exist")
            print('')
            sys.exit(1)

    def print_status(self):
        """Print clowder repo status"""
        repo_path = os.path.join(self.root_directory, '.clowder')
        # FIXME: Probably should remove this as it assumes .clowder repo which isn't git directory
        if not os.path.isdir(os.path.join(repo_path, '.git')):
            output = colored('.clowder', 'green')
            print(output)
            return
        print(' - Fetching upstream changes for clowder repo')
        print('')
        git_fetch(self.clowder_path)
        project_output = format_project_string(repo_path, '.clowder')
        current_ref_output = format_ref_string(repo_path)

        clowder_symlink = os.path.join(self.root_directory, 'clowder.yaml')
        if os.path.islink(clowder_symlink):
            real_path = os.path.realpath(clowder_symlink)
            clowder_path = remove_prefix(real_path + '/', self.root_directory)
            path_output = colored(clowder_path[1:-1], 'cyan')
            print(project_output + ' ' + current_ref_output + ' -> ' + path_output)
        else:
            print(project_output + ' ' + current_ref_output)

    def pull(self):
        """Pull clowder repo upstream changes"""
        git_pull(self.clowder_path)

    def push(self):
        """Push clowder repo changes"""
        git_push(self.clowder_path)

    def run_command(self, command):
        """Run command in clowder repo"""
        command_output = colored('$ ' + command, attrs=['bold'])
        print(command_output)
        subprocess.call(command.split(), cwd=self.clowder_path)

    def status(self):
        """Print clowder repo git status"""
        git_status(self.clowder_path)

    def _validate(self):
        """Validate status of clowder repo"""
        if not validate_repo_state(self.clowder_path):
            print_validation(self.clowder_path)
            print('')
            sys.exit(1)
<commit_msg>Distinguish path pointers for projects and clowder repo<commit_after>"""Clowder repo management"""
import os
import subprocess
import sys
from termcolor import colored
from clowder.utility.git_utilities import (
    git_add,
    git_branches,
    git_checkout,
    git_commit,
    git_create_repo,
    git_fetch,
    git_is_dirty,
    git_pull,
    git_push,
    git_reset_head,
    git_status
)
from clowder.utility.clowder_utilities import (
    force_symlink,
    format_project_string,
    format_ref_string,
    print_validation,
    remove_prefix,
    validate_repo_state
)

class ClowderRepo(object):
    """Class encapsulating clowder repo information"""
    def __init__(self, root_directory):
        self.root_directory = root_directory
        self.clowder_path = os.path.join(self.root_directory, '.clowder')

    def add(self, files):
        """Add files in clowder repo to git index"""
        git_add(self.clowder_path, files)
        git_status(self.clowder_path)

    def branches(self):
        """Return current local branches"""
        return git_branches(self.clowder_path)

    def checkout(self, ref):
        """Checkout ref in clowder repo"""
        if self.is_dirty():
            print(' - Dirty repo. Please stash, commit, or discard your changes')
        else:
            git_checkout(self.clowder_path, ref)

    def clean(self):
        """Discard changes in clowder repo"""
        if self.is_dirty():
            print(' - Discard current changes')
            git_reset_head(self.clowder_path)
        else:
            print(' - No changes to discard')

    def commit(self, message):
        """Commit current changes in clowder repo"""
        git_commit(self.clowder_path, message)

    def init(self, url, branch):
        """Clone clowder repo from url"""
        repo_branch = 'refs/heads/' + branch
        git_create_repo(url, self.clowder_path, 'origin', repo_branch)
        self.link()

    def is_dirty(self):
        """Check if project is dirty"""
        return git_is_dirty(self.clowder_path)

    def link(self, version=None):
        """Create symlink pointing to clowder.yaml file"""
        if version is None:
            yaml_file = os.path.join(self.root_directory, '.clowder', 'clowder.yaml')
            path_output = colored('.clowder/clowder.yaml', 'cyan')
        else:
            relative_path = os.path.join('.clowder', 'versions', version, 'clowder.yaml')
            path_output = colored(relative_path, 'cyan')
            yaml_file = os.path.join(self.root_directory, relative_path)

        if os.path.isfile(yaml_file):
            yaml_symlink = os.path.join(self.root_directory, 'clowder.yaml')
            print(' - Symlink ' + path_output)
            force_symlink(yaml_file, yaml_symlink)
        else:
            print(path_output + " doesn't seem to exist")
            print('')
            sys.exit(1)

    def print_status(self):
        """Print clowder repo status"""
        repo_path = os.path.join(self.root_directory, '.clowder')
        # FIXME: Probably should remove this as it assumes .clowder repo which isn't git directory
        if not os.path.isdir(os.path.join(repo_path, '.git')):
            output = colored('.clowder', 'green')
            print(output)
            return
        print(' - Fetching upstream changes for clowder repo')
        print('')
        git_fetch(self.clowder_path)
        project_output = format_project_string(repo_path, '.clowder')
        current_ref_output = format_ref_string(repo_path)

        clowder_symlink = os.path.join(self.root_directory, 'clowder.yaml')
        if os.path.islink(clowder_symlink):
            real_path = os.path.realpath(clowder_symlink)
            clowder_path = remove_prefix(real_path + '/', self.root_directory)
            path_output = colored(clowder_path[1:-1], 'cyan')
            print(project_output + ' ' + current_ref_output + ' ~~> ' + path_output)
        else:
            print(project_output + ' ' + current_ref_output)

    def pull(self):
        """Pull clowder repo upstream changes"""
        git_pull(self.clowder_path)

    def push(self):
        """Push clowder repo changes"""
        git_push(self.clowder_path)

    def run_command(self, command):
        """Run command in clowder repo"""
        command_output = colored('$ ' + command, attrs=['bold'])
        print(command_output)
        subprocess.call(command.split(), cwd=self.clowder_path)

    def status(self):
        """Print clowder repo git status"""
        git_status(self.clowder_path)

    def _validate(self):
        """Validate status of clowder repo"""
        if not validate_repo_state(self.clowder_path):
            print_validation(self.clowder_path)
            print('')
            sys.exit(1)
<|endoftext|>


<commit_before>
VERSION = '0.23.5.3'

<commit_msg>version bump for 0.23.5.4.<commit_after>
VERSION = '0.23.5.4'

<|endoftext|>