<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

# This work was created by participants in the DataONE project, and is
# jointly copyrighted by participating institutions in DataONE. For
# more information on DataONE, see our web site at http://dataone.org.
#
#   Copyright 2009-2016 DataONE
#
# 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
#
#   http://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.
'''
Module d1_common.tests.test_url
===============================

:Synopsis: Unit tests for url.py
:Created: 2011-01-21
:Author: DataONE (Vieglais, Dahl)
'''

# Stdlib.
import codecs
import logging
import os
import sys
import unittest

# D1.
from d1_common import xmlrunner
import d1_common.url

# App

class TestUrl(unittest.TestCase):
  def test_010(self):
    '''encodePathElement()'''
    fpath = os.path.abspath(os.path.dirname(__file__))
    ftest = os.path.join(fpath, 'd1_testdocs/encodingTestSet/testUnicodeStrings.utf8.txt')
    testfile = codecs.open(ftest, encoding='utf-8', mode='r')
    testrows = testfile.readlines()
    for row in testrows:
      parts = row.split('\t')
      if len(parts) > 1:
        v = parts[0]
        if v.startswith('common') or v.startswith('path'):
          e = parts[1].strip()
          self.assertEqual(e, d1_common.url.encodePathElement(v))

  def test_020(self):
    '''encodeQueryElement()'''
    fpath = os.path.abspath(os.path.dirname(__file__))
    ftest = os.path.join(fpath, 'd1_testdocs/encodingTestSet/testUnicodeStrings.utf8.txt')
    testfile = codecs.open(ftest, encoding='utf-8', mode='r')
    testrows = testfile.readlines()
    for row in testrows:
      parts = row.split('\t')
      if len(parts) > 1:
        v = parts[0]
        if v.startswith('common') or v.startswith('query'):
          e = parts[1].strip()
          self.assertEqual(e, d1_common.url.encodeQueryElement(v))

  def test_030(self):
    '''urlencode()'''
    data = [
      ('a', '"#<>[]^`{}|'), ('b', '-&=&='), ('c', 'http://example.com/data/mydata?row=24')
    ]
    expected = 'a=%22%23%3C%3E%5B%5D%5E%60%7B%7D%7C&b=-%26%3D%26%3D&c=http://example.com/data/mydata?row%3D24'
    test = d1_common.url.urlencode(data)
    self.assertEqual(test, expected)

  def test_040(self):
    '''stripElementSlashes()'''
    self.assertEqual('element', d1_common.url.stripElementSlashes('/element'))
    self.assertEqual('element', d1_common.url.stripElementSlashes('//element/'))
    self.assertEqual('element', d1_common.url.stripElementSlashes('element/'))
    self.assertEqual('ele/ment', d1_common.url.stripElementSlashes('/ele/ment/'))
    self.assertEqual('ele//ment', d1_common.url.stripElementSlashes('ele//ment'))
    self.assertEqual('', d1_common.url.stripElementSlashes('/'))
    self.assertEqual('', d1_common.url.stripElementSlashes('//'))

  def test_050(self):
    '''joinPathElements()'''
    self.assertEqual('a/b', d1_common.url.joinPathElements('a', 'b'))
    self.assertEqual('a/b/c', d1_common.url.joinPathElements('a/', '/b', 'c'))

  def test_055(self):
    '''joinPathElementsNoStrip()'''
    self.assertEqual('', d1_common.url.joinPathElementsNoStrip(''))
    self.assertEqual('/', d1_common.url.joinPathElementsNoStrip('/'))
    self.assertEqual('//', d1_common.url.joinPathElementsNoStrip('//'))
    self.assertEqual('ab', d1_common.url.joinPathElementsNoStrip('ab'))
    self.assertEqual('a/b', d1_common.url.joinPathElementsNoStrip('a/b'))
    self.assertEqual('a/b', d1_common.url.joinPathElementsNoStrip('a', 'b'))
    self.assertEqual('a/b/c', d1_common.url.joinPathElementsNoStrip('a', 'b', 'c'))
    self.assertEqual('a/b', d1_common.url.joinPathElementsNoStrip('a/', 'b'))
    self.assertEqual('a//b', d1_common.url.joinPathElementsNoStrip('a//', 'b'))
    self.assertEqual('a/b/', d1_common.url.joinPathElementsNoStrip('a/', 'b/'))
    self.assertEqual(
      'a//b///c', d1_common.url.joinPathElementsNoStrip(
        'a//', 'b', '///c'
      )
    )

  def test_060(self):
    '''normalizeTarget()'''
    u0 = "http://some.server/base/mn/"
    u1 = "http://some.server/base/mn"
    u2 = "http://some.server/base/mn?"
    u3 = "http://some.server/base/mn/?"
    self.assertEqual(u0, d1_common.url.normalizeTarget(u0))
    self.assertEqual(u0, d1_common.url.normalizeTarget(u1))
    self.assertEqual(u0, d1_common.url.normalizeTarget(u2))
    self.assertEqual(u0, d1_common.url.normalizeTarget(u3))

  def test_070(self):
    '''makeCNBaseURL()'''
    self.assertEqual(d1_common.url.makeCNBaseURL(''), 'https://cn.dataone.org/cn')
    self.assertEqual(d1_common.url.makeCNBaseURL('test.com'), 'https://test.com/cn')
    self.assertEqual(d1_common.url.makeCNBaseURL('test.com/cn'), 'https://test.com/cn')
    self.assertEqual(
      d1_common.url.makeCNBaseURL(
        'test.com/a/cn'
      ), 'https://test.com/a/cn'
    )
    self.assertEqual(d1_common.url.makeCNBaseURL('http://test.com'), 'http://test.com/cn')
    self.assertEqual(d1_common.url.makeCNBaseURL('http://test.com/'), 'http://test.com/')
    self.assertEqual(
      d1_common.url.makeCNBaseURL(
        'http://test.com/cn'
      ), 'http://test.com/cn'
    )
    self.assertEqual(
      d1_common.url.makeCNBaseURL(
        'http://test.com/a/b/c/cn'
      ), 'http://test.com/a/b/c/cn'
    )

  def test_080(self):
    '''makeMNBaseURL()'''
    self.assertEqual(d1_common.url.makeMNBaseURL(''), 'https://localhost/mn')
    self.assertEqual(d1_common.url.makeMNBaseURL('test.com'), 'https://test.com/mn')
    self.assertEqual(d1_common.url.makeMNBaseURL('test.com/mn'), 'https://test.com/mn')
    self.assertEqual(
      d1_common.url.makeMNBaseURL(
        'test.com/a/mn'
      ), 'https://test.com/a/mn'
    )
    self.assertEqual(d1_common.url.makeMNBaseURL('http://test.com'), 'http://test.com/mn')
    self.assertEqual(d1_common.url.makeMNBaseURL('http://test.com/'), 'http://test.com/')
    self.assertEqual(
      d1_common.url.makeMNBaseURL(
        'http://test.com/mn'
      ), 'http://test.com/mn'
    )
    self.assertEqual(
      d1_common.url.makeMNBaseURL(
        'http://test.com/a/b/c/mn'
      ), 'http://test.com/a/b/c/mn'
    )

#===============================================================================

def log_setup():
  formatter = logging.Formatter(
    '%(asctime)s %(levelname)-8s %(message)s', '%y/%m/%d %H:%M:%S'
  )
  console_logger = logging.StreamHandler(sys.stdout)
  console_logger.setFormatter(formatter)
  logging.getLogger('').addHandler(console_logger)

def main():
  import optparse

  log_setup()

  # Command line opts.
  parser = optparse.OptionParser()
  parser.add_option('--debug', action='store_true', default=False, dest='debug')
  parser.add_option(
    '--test', action='store',
    default='',
    dest='test',
    help='run a single test'
  )

  (options, arguments) = parser.parse_args()

  if options.debug:
    logging.getLogger('').setLevel(logging.DEBUG)
  else:
    logging.getLogger('').setLevel(logging.ERROR)

  s = TestUrl
  s.options = options

  if options.test != '':
    suite = unittest.TestSuite(map(s, [options.test]))
  else:
    suite = unittest.TestLoader().loadTestsFromTestCase(s)

  unittest.TextTestRunner(verbosity=2).run(suite)

if __name__ == '__main__':
  main()
<commit_msg>Move testUnicodeStrings.utf8.txt to test folder<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

# This work was created by participants in the DataONE project, and is
# jointly copyrighted by participating institutions in DataONE. For
# more information on DataONE, see our web site at http://dataone.org.
#
#   Copyright 2009-2016 DataONE
#
# 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
#
#   http://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.
"""
Module d1_common.tests.test_url
===============================

:Synopsis: Unit tests for url.py
:Created: 2011-01-21
:Author: DataONE (Vieglais, Dahl)
"""

# Stdlib.
import codecs
import logging
import os
import sys
import unittest

# D1.
import d1_common.url

# App
HERE_DIR_PATH = os.path.abspath(os.path.dirname(__file__))

class TestUrl(unittest.TestCase):
  def test_010(self):
    """encodePathElement()"""
    ftest = os.path.join(HERE_DIR_PATH, 'testUnicodeStrings.utf8.txt')
    testfile = codecs.open(ftest, encoding='utf-8', mode='r')
    testrows = testfile.readlines()
    for row in testrows:
      parts = row.split('\t')
      if len(parts) > 1:
        v = parts[0]
        if v.startswith('common') or v.startswith('path'):
          e = parts[1].strip()
          self.assertEqual(e, d1_common.url.encodePathElement(v))

  def test_020(self):
    """encodeQueryElement()"""
    fpath = os.path.abspath(os.path.dirname(__file__))
    ftest = os.path.join(fpath, 'testUnicodeStrings.utf8.txt')
    testfile = codecs.open(ftest, encoding='utf-8', mode='r')
    testrows = testfile.readlines()
    for row in testrows:
      parts = row.split('\t')
      if len(parts) > 1:
        v = parts[0]
        if v.startswith('common') or v.startswith('query'):
          e = parts[1].strip()
          self.assertEqual(e, d1_common.url.encodeQueryElement(v))

  def test_030(self):
    """urlencode()"""
    data = [
      ('a', '"#<>[]^`{}|'), ('b', '-&=&='), ('c', 'http://example.com/data/mydata?row=24')
    ]
    expected = 'a=%22%23%3C%3E%5B%5D%5E%60%7B%7D%7C&b=-%26%3D%26%3D&c=http://example.com/data/mydata?row%3D24'
    test = d1_common.url.urlencode(data)
    self.assertEqual(test, expected)

  def test_040(self):
    """stripElementSlashes()"""
    self.assertEqual('element', d1_common.url.stripElementSlashes('/element'))
    self.assertEqual('element', d1_common.url.stripElementSlashes('//element/'))
    self.assertEqual('element', d1_common.url.stripElementSlashes('element/'))
    self.assertEqual('ele/ment', d1_common.url.stripElementSlashes('/ele/ment/'))
    self.assertEqual('ele//ment', d1_common.url.stripElementSlashes('ele//ment'))
    self.assertEqual('', d1_common.url.stripElementSlashes('/'))
    self.assertEqual('', d1_common.url.stripElementSlashes('//'))

  def test_050(self):
    """joinPathElements()"""
    self.assertEqual('a/b', d1_common.url.joinPathElements('a', 'b'))
    self.assertEqual('a/b/c', d1_common.url.joinPathElements('a/', '/b', 'c'))

  def test_055(self):
    """joinPathElementsNoStrip()"""
    self.assertEqual('', d1_common.url.joinPathElementsNoStrip(''))
    self.assertEqual('/', d1_common.url.joinPathElementsNoStrip('/'))
    self.assertEqual('//', d1_common.url.joinPathElementsNoStrip('//'))
    self.assertEqual('ab', d1_common.url.joinPathElementsNoStrip('ab'))
    self.assertEqual('a/b', d1_common.url.joinPathElementsNoStrip('a/b'))
    self.assertEqual('a/b', d1_common.url.joinPathElementsNoStrip('a', 'b'))
    self.assertEqual('a/b/c', d1_common.url.joinPathElementsNoStrip('a', 'b', 'c'))
    self.assertEqual('a/b', d1_common.url.joinPathElementsNoStrip('a/', 'b'))
    self.assertEqual('a//b', d1_common.url.joinPathElementsNoStrip('a//', 'b'))
    self.assertEqual('a/b/', d1_common.url.joinPathElementsNoStrip('a/', 'b/'))
    self.assertEqual(
      'a//b///c', d1_common.url.joinPathElementsNoStrip(
        'a//', 'b', '///c'
      )
    )

  def test_060(self):
    """normalizeTarget()"""
    u0 = "http://some.server/base/mn/"
    u1 = "http://some.server/base/mn"
    u2 = "http://some.server/base/mn?"
    u3 = "http://some.server/base/mn/?"
    self.assertEqual(u0, d1_common.url.normalizeTarget(u0))
    self.assertEqual(u0, d1_common.url.normalizeTarget(u1))
    self.assertEqual(u0, d1_common.url.normalizeTarget(u2))
    self.assertEqual(u0, d1_common.url.normalizeTarget(u3))

  def test_070(self):
    """makeCNBaseURL()"""
    self.assertEqual(d1_common.url.makeCNBaseURL(''), 'https://cn.dataone.org/cn')
    self.assertEqual(d1_common.url.makeCNBaseURL('test.com'), 'https://test.com/cn')
    self.assertEqual(d1_common.url.makeCNBaseURL('test.com/cn'), 'https://test.com/cn')
    self.assertEqual(
      d1_common.url.makeCNBaseURL(
        'test.com/a/cn'
      ), 'https://test.com/a/cn'
    )
    self.assertEqual(d1_common.url.makeCNBaseURL('http://test.com'), 'http://test.com/cn')
    self.assertEqual(d1_common.url.makeCNBaseURL('http://test.com/'), 'http://test.com/')
    self.assertEqual(
      d1_common.url.makeCNBaseURL(
        'http://test.com/cn'
      ), 'http://test.com/cn'
    )
    self.assertEqual(
      d1_common.url.makeCNBaseURL(
        'http://test.com/a/b/c/cn'
      ), 'http://test.com/a/b/c/cn'
    )

  def test_080(self):
    """makeMNBaseURL()"""
    self.assertEqual(d1_common.url.makeMNBaseURL(''), 'https://localhost/mn')
    self.assertEqual(d1_common.url.makeMNBaseURL('test.com'), 'https://test.com/mn')
    self.assertEqual(d1_common.url.makeMNBaseURL('test.com/mn'), 'https://test.com/mn')
    self.assertEqual(
      d1_common.url.makeMNBaseURL(
        'test.com/a/mn'
      ), 'https://test.com/a/mn'
    )
    self.assertEqual(d1_common.url.makeMNBaseURL('http://test.com'), 'http://test.com/mn')
    self.assertEqual(d1_common.url.makeMNBaseURL('http://test.com/'), 'http://test.com/')
    self.assertEqual(
      d1_common.url.makeMNBaseURL(
        'http://test.com/mn'
      ), 'http://test.com/mn'
    )
    self.assertEqual(
      d1_common.url.makeMNBaseURL(
        'http://test.com/a/b/c/mn'
      ), 'http://test.com/a/b/c/mn'
    )

#===============================================================================

def log_setup():
  formatter = logging.Formatter(
    '%(asctime)s %(levelname)-8s %(message)s', '%y/%m/%d %H:%M:%S'
  )
  console_logger = logging.StreamHandler(sys.stdout)
  console_logger.setFormatter(formatter)
  logging.getLogger('').addHandler(console_logger)

def main():
  import optparse

  log_setup()

  # Command line opts.
  parser = optparse.OptionParser()
  parser.add_option('--debug', action='store_true', default=False, dest='debug')
  parser.add_option(
    '--test', action='store',
    default='',
    dest='test',
    help='run a single test'
  )

  (options, arguments) = parser.parse_args()

  if options.debug:
    logging.getLogger('').setLevel(logging.DEBUG)
  else:
    logging.getLogger('').setLevel(logging.ERROR)

  s = TestUrl
  s.options = options

  if options.test != '':
    suite = unittest.TestSuite(map(s, [options.test]))
  else:
    suite = unittest.TestLoader().loadTestsFromTestCase(s)

  unittest.TextTestRunner(verbosity=2).run(suite)

if __name__ == '__main__':
  main()
<|endoftext|>


<commit_before>import asyncio
import discord
from   datetime    import datetime
from   discord.ext import commands
from   shutil      import copyfile
import time
import json
import os
import re
from   Cogs        import DisplayName
from   Cogs        import Nullify

class Welcome:

    def __init__(self, bot, settings):
        self.bot = bot
        self.settings = settings
        self.regexUserName = re.compile(r"\[\[[user]+\]\]", re.IGNORECASE)
        self.regexUserPing = re.compile(r"\[\[[atuser]+\]\]", re.IGNORECASE)
        self.regexServer   = re.compile(r"\[\[[server]+\]\]", re.IGNORECASE)

    async def onjoin(self, member, server):
        # Welcome
        welcomeChannel = self.settings.getServerStat(server, "WelcomeChannel")
        if welcomeChannel:
            for channel in server.channels:
                if str(channel.id) == str(welcomeChannel):
                    welcomeChannel = channel
                    break
        if welcomeChannel:
            await self._welcome(member, server, welcomeChannel)
        else:
            await self._welcome(member, server)

    async def onleave(self, member, server):
        # Goodbye
        welcomeChannel = self.settings.getServerStat(server, "WelcomeChannel")
        if welcomeChannel:
            for channel in server.channels:
                if str(channel.id) == str(welcomeChannel):
                    welcomeChannel = channel
                    break
        if welcomeChannel:
            await self._goodbye(member, server, welcomeChannel)
        else:
            await self._goodbye(member, server)

    @commands.command(pass_context=True)
    async def setwelcome(self, ctx, *, message = None):
        """Sets the welcome message for your server (bot-admin only). [[user]] = user name, [[atuser]] = user mention, [[server]] = server name"""

        isAdmin = ctx.message.author.permissions_in(ctx.message.channel).administrator
        if not isAdmin:
            checkAdmin = self.settings.getServerStat(ctx.message.guild, "AdminArray")
            for role in ctx.message.author.roles:
                for aRole in checkAdmin:
                    # Get the role that corresponds to the id
                    if str(aRole['ID']) == str(role.id):
                        isAdmin = True
        # Only allow admins to change server stats
        if not isAdmin:
            await ctx.channel.send('You do not have sufficient privileges to access this command.')
            return

        if message == None:
            self.settings.setServerStat(ctx.message.guild, "Welcome", None)
            await ctx.channel.send('Welcome message removed!')
            return

        self.settings.setServerStat(ctx.message.guild, "Welcome", message)
        await ctx.channel.send('Welcome message updated!\n\nHere\'s a preview:')
        await self._welcome(ctx.message.author, ctx.message.guild, ctx.message.channel)

    @commands.command(pass_context=True)
    async def testwelcome(self, ctx, *, member = None):
        """Prints the current welcome message (bot-admin only)."""

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

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

        # Only allow admins to change server stats
        if not isAdmin:
            await ctx.channel.send('You do not have sufficient privileges to access this command.')
            return

        if member == None:
            member = ctx.message.author
        if type(member) is str:
            memberName = member
            member = DisplayName.memberForName(memberName, ctx.message.guild)
            if not member:
                msg = 'I couldn\'t find *{}*...'.format(memberName)
                # Check for suppress
                if suppress:
                    msg = Nullify.clean(msg)
                await ctx.channel.send(msg)
                return
        # Here we have found a member, and stuff.
        # Let's make sure we have a message
        message = self.settings.getServerStat(ctx.message.guild, "Welcome")
        if message == None:
            await ctx.channel.send('Welcome message not setup.  You can do so with the `{}setwelcome [message]` command.'.format(ctx.prefix))
            return
        await self._welcome(member, ctx.message.guild, ctx.message.channel)
        # Print the welcome channel
        welcomeChannel = self.settings.getServerStat(ctx.message.guild, "WelcomeChannel")
        if welcomeChannel:
            for channel in ctx.message.guild.channels:
                if str(channel.id) == str(welcomeChannel):
                    welcomeChannel = channel
                    break
        if welcomeChannel:
            msg = 'The current welcome channel is **{}**.'.format(welcomeChannel.name)
        else:
            msg = 'The current welcome channel is the server\'s default channel (**{}**).'.format(ctx.message.guild.default_channel.name)
        await ctx.channel.send(msg)

    @commands.command(pass_context=True)
    async def setgoodbye(self, ctx, *, message = None):
        """Sets the goodbye message for your server (bot-admin only). [[user]] = user name, [[atuser]] = user mention, [[server]] = server name"""

        isAdmin = ctx.message.author.permissions_in(ctx.message.channel).administrator
        if not isAdmin:
            checkAdmin = self.settings.getServerStat(ctx.message.guild, "AdminArray")
            for role in ctx.message.author.roles:
                for aRole in checkAdmin:
                    # Get the role that corresponds to the id
                    if str(aRole['ID']) == str(role.id):
                        isAdmin = True
        # Only allow admins to change server stats
        if not isAdmin:
            await ctx.channel.send('You do not have sufficient privileges to access this command.')
            return

        if message == None:
            self.settings.setServerStat(ctx.message.guild, "Goodbye", None)
            await ctx.channel.send('Goodbye message removed!')
            return

        self.settings.setServerStat(ctx.message.guild, "Goodbye", message)
        await ctx.channel.send('Goodbye message updated!\n\nHere\'s a preview:')
        await self._goodbye(ctx.message.author, ctx.message.guild, ctx.message.channel)

    @commands.command(pass_context=True)
    async def testgoodbye(self, ctx, *, member = None):
        """Prints the current goodbye message (bot-admin only)."""

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

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

        # Only allow admins to change server stats
        if not isAdmin:
            await ctx.channel.send('You do not have sufficient privileges to access this command.')
            return

        if member == None:
            member = ctx.message.author
        if type(member) is str:
            memberName = member
            member = DisplayName.memberForName(memberName, ctx.message.guild)
            if not member:
                msg = 'I couldn\'t find *{}*...'.format(memberName)
                # Check for suppress
                if suppress:
                    msg = Nullify.clean(msg)
                await ctx.channel.send(msg)
                return
        # Here we have found a member, and stuff.
        # Let's make sure we have a message
        message = self.settings.getServerStat(ctx.message.guild, "Goodbye")
        if message == None:
            await ctx.channel.send('Goodbye message not setup.  You can do so with the `{}setgoodbye [message]` command.'.format(ctx.prefix))
            return
        await self._goodbye(member, ctx.message.guild, ctx.message.channel)

        # Print the goodbye channel
        welcomeChannel = self.settings.getServerStat(ctx.message.guild, "WelcomeChannel")
        if welcomeChannel:
            for channel in ctx.message.guild.channels:
                if str(channel.id) == str(welcomeChannel):
                    welcomeChannel = channel
                    break
        if welcomeChannel:
            msg = 'The current goodbye channel is **{}**.'.format(welcomeChannel.name)
        else:
            msg = 'The current goodbye channel is the server\'s default channel (**{}**).'.format(ctx.message.guild.default_channel.name)
        await ctx.channel.send(msg)

    async def _welcome(self, member, server, channel = None):
        # Check if we're suppressing @here and @everyone mentions
        if self.settings.getServerStat(server, "SuppressMentions").lower() == "yes":
            suppress = True
        else:
            suppress = False
        message = self.settings.getServerStat(server, "Welcome")
        if message == None:
            return
        # Let's regex and replace [[user]] [[atuser]] and [[server]]
        message = re.sub(self.regexUserName, "{}".format(DisplayName.name(member)), message)
        message = re.sub(self.regexUserPing, "{}".format(member.mention), message)
        message = re.sub(self.regexServer,   "{}".format(server.name), message)

        if suppress:
            message = Nullify.clean(message)

        print("{}:{}".format(channel, server))

        if channel:
            await channel.send(message)
        else:
            await server.default_channel.send(message)

    async def _goodbye(self, member, server, channel = None):
        # Check if we're suppressing @here and @everyone mentions
        if self.settings.getServerStat(server, "SuppressMentions").lower() == "yes":
            suppress = True
        else:
            suppress = False
        message = self.settings.getServerStat(server, "Goodbye")
        if message == None:
            return
        # Let's regex and replace [[user]] [[atuser]] and [[server]]
        message = re.sub(self.regexUserName, "{}".format(DisplayName.name(member)), message)
        message = re.sub(self.regexUserPing, "{}".format(member.mention), message)
        message = re.sub(self.regexServer,   "{}".format(server.name), message)

        if suppress:
            message = Nullify.clean(message)
        if channel:
            await channel.send(message)
        else:
            await server.default_channel.send(message)

    @commands.command(pass_context=True)
    async def setwelcomechannel(self, ctx, *, channel : discord.TextChannel = None):
        """Sets the channel for the welcome and goodbye messages (bot-admin only)."""

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

        # Only allow admins to change server stats
        if not isAdmin:
            await ctx.channel.send('You do not have sufficient privileges to access this command.')
            return

        if channel == None:
            self.settings.setServerStat(ctx.message.guild, "WelcomeChannel", "")
            msg = 'Welcome and goodbye messages will be displayed in the default channel (**{}**).'.format(ctx.message.guild.default_channel.name)
            await ctx.channel.send(msg)
            return

        if type(channel) is str:
            try:
                role = discord.utils.get(message.guild.channels, name=role)
            except:
                print("That channel does not exist")
                return

        # If we made it this far - then we can add it
        self.settings.setServerStat(ctx.message.guild, "WelcomeChannel", channel.id)

        msg = 'Welcome and goodbye messages will be displayed in **{}**.'.format(channel.name)
        await ctx.channel.send(msg)

    @setwelcomechannel.error
    async def setwelcomechannel_error(self, ctx, error):
        # do stuff
        msg = 'setwelcomechannel Error: {}'.format(ctx)
        await error.channel.send(msg)
<commit_msg>Fixed extra print()<commit_after>import asyncio
import discord
from   datetime    import datetime
from   discord.ext import commands
from   shutil      import copyfile
import time
import json
import os
import re
from   Cogs        import DisplayName
from   Cogs        import Nullify

class Welcome:

    def __init__(self, bot, settings):
        self.bot = bot
        self.settings = settings
        self.regexUserName = re.compile(r"\[\[[user]+\]\]", re.IGNORECASE)
        self.regexUserPing = re.compile(r"\[\[[atuser]+\]\]", re.IGNORECASE)
        self.regexServer   = re.compile(r"\[\[[server]+\]\]", re.IGNORECASE)

    async def onjoin(self, member, server):
        # Welcome
        welcomeChannel = self.settings.getServerStat(server, "WelcomeChannel")
        if welcomeChannel:
            for channel in server.channels:
                if str(channel.id) == str(welcomeChannel):
                    welcomeChannel = channel
                    break
        if welcomeChannel:
            await self._welcome(member, server, welcomeChannel)
        else:
            await self._welcome(member, server)

    async def onleave(self, member, server):
        # Goodbye
        welcomeChannel = self.settings.getServerStat(server, "WelcomeChannel")
        if welcomeChannel:
            for channel in server.channels:
                if str(channel.id) == str(welcomeChannel):
                    welcomeChannel = channel
                    break
        if welcomeChannel:
            await self._goodbye(member, server, welcomeChannel)
        else:
            await self._goodbye(member, server)

    @commands.command(pass_context=True)
    async def setwelcome(self, ctx, *, message = None):
        """Sets the welcome message for your server (bot-admin only). [[user]] = user name, [[atuser]] = user mention, [[server]] = server name"""

        isAdmin = ctx.message.author.permissions_in(ctx.message.channel).administrator
        if not isAdmin:
            checkAdmin = self.settings.getServerStat(ctx.message.guild, "AdminArray")
            for role in ctx.message.author.roles:
                for aRole in checkAdmin:
                    # Get the role that corresponds to the id
                    if str(aRole['ID']) == str(role.id):
                        isAdmin = True
        # Only allow admins to change server stats
        if not isAdmin:
            await ctx.channel.send('You do not have sufficient privileges to access this command.')
            return

        if message == None:
            self.settings.setServerStat(ctx.message.guild, "Welcome", None)
            await ctx.channel.send('Welcome message removed!')
            return

        self.settings.setServerStat(ctx.message.guild, "Welcome", message)
        await ctx.channel.send('Welcome message updated!\n\nHere\'s a preview:')
        await self._welcome(ctx.message.author, ctx.message.guild, ctx.message.channel)

    @commands.command(pass_context=True)
    async def testwelcome(self, ctx, *, member = None):
        """Prints the current welcome message (bot-admin only)."""

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

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

        # Only allow admins to change server stats
        if not isAdmin:
            await ctx.channel.send('You do not have sufficient privileges to access this command.')
            return

        if member == None:
            member = ctx.message.author
        if type(member) is str:
            memberName = member
            member = DisplayName.memberForName(memberName, ctx.message.guild)
            if not member:
                msg = 'I couldn\'t find *{}*...'.format(memberName)
                # Check for suppress
                if suppress:
                    msg = Nullify.clean(msg)
                await ctx.channel.send(msg)
                return
        # Here we have found a member, and stuff.
        # Let's make sure we have a message
        message = self.settings.getServerStat(ctx.message.guild, "Welcome")
        if message == None:
            await ctx.channel.send('Welcome message not setup.  You can do so with the `{}setwelcome [message]` command.'.format(ctx.prefix))
            return
        await self._welcome(member, ctx.message.guild, ctx.message.channel)
        # Print the welcome channel
        welcomeChannel = self.settings.getServerStat(ctx.message.guild, "WelcomeChannel")
        if welcomeChannel:
            for channel in ctx.message.guild.channels:
                if str(channel.id) == str(welcomeChannel):
                    welcomeChannel = channel
                    break
        if welcomeChannel:
            msg = 'The current welcome channel is **{}**.'.format(welcomeChannel.name)
        else:
            msg = 'The current welcome channel is the server\'s default channel (**{}**).'.format(ctx.message.guild.default_channel.name)
        await ctx.channel.send(msg)

    @commands.command(pass_context=True)
    async def setgoodbye(self, ctx, *, message = None):
        """Sets the goodbye message for your server (bot-admin only). [[user]] = user name, [[atuser]] = user mention, [[server]] = server name"""

        isAdmin = ctx.message.author.permissions_in(ctx.message.channel).administrator
        if not isAdmin:
            checkAdmin = self.settings.getServerStat(ctx.message.guild, "AdminArray")
            for role in ctx.message.author.roles:
                for aRole in checkAdmin:
                    # Get the role that corresponds to the id
                    if str(aRole['ID']) == str(role.id):
                        isAdmin = True
        # Only allow admins to change server stats
        if not isAdmin:
            await ctx.channel.send('You do not have sufficient privileges to access this command.')
            return

        if message == None:
            self.settings.setServerStat(ctx.message.guild, "Goodbye", None)
            await ctx.channel.send('Goodbye message removed!')
            return

        self.settings.setServerStat(ctx.message.guild, "Goodbye", message)
        await ctx.channel.send('Goodbye message updated!\n\nHere\'s a preview:')
        await self._goodbye(ctx.message.author, ctx.message.guild, ctx.message.channel)

    @commands.command(pass_context=True)
    async def testgoodbye(self, ctx, *, member = None):
        """Prints the current goodbye message (bot-admin only)."""

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

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

        # Only allow admins to change server stats
        if not isAdmin:
            await ctx.channel.send('You do not have sufficient privileges to access this command.')
            return

        if member == None:
            member = ctx.message.author
        if type(member) is str:
            memberName = member
            member = DisplayName.memberForName(memberName, ctx.message.guild)
            if not member:
                msg = 'I couldn\'t find *{}*...'.format(memberName)
                # Check for suppress
                if suppress:
                    msg = Nullify.clean(msg)
                await ctx.channel.send(msg)
                return
        # Here we have found a member, and stuff.
        # Let's make sure we have a message
        message = self.settings.getServerStat(ctx.message.guild, "Goodbye")
        if message == None:
            await ctx.channel.send('Goodbye message not setup.  You can do so with the `{}setgoodbye [message]` command.'.format(ctx.prefix))
            return
        await self._goodbye(member, ctx.message.guild, ctx.message.channel)

        # Print the goodbye channel
        welcomeChannel = self.settings.getServerStat(ctx.message.guild, "WelcomeChannel")
        if welcomeChannel:
            for channel in ctx.message.guild.channels:
                if str(channel.id) == str(welcomeChannel):
                    welcomeChannel = channel
                    break
        if welcomeChannel:
            msg = 'The current goodbye channel is **{}**.'.format(welcomeChannel.name)
        else:
            msg = 'The current goodbye channel is the server\'s default channel (**{}**).'.format(ctx.message.guild.default_channel.name)
        await ctx.channel.send(msg)

    async def _welcome(self, member, server, channel = None):
        # Check if we're suppressing @here and @everyone mentions
        if self.settings.getServerStat(server, "SuppressMentions").lower() == "yes":
            suppress = True
        else:
            suppress = False
        message = self.settings.getServerStat(server, "Welcome")
        if message == None:
            return
        # Let's regex and replace [[user]] [[atuser]] and [[server]]
        message = re.sub(self.regexUserName, "{}".format(DisplayName.name(member)), message)
        message = re.sub(self.regexUserPing, "{}".format(member.mention), message)
        message = re.sub(self.regexServer,   "{}".format(server.name), message)

        if suppress:
            message = Nullify.clean(message)

        if channel:
            await channel.send(message)
        else:
            await server.default_channel.send(message)

    async def _goodbye(self, member, server, channel = None):
        # Check if we're suppressing @here and @everyone mentions
        if self.settings.getServerStat(server, "SuppressMentions").lower() == "yes":
            suppress = True
        else:
            suppress = False
        message = self.settings.getServerStat(server, "Goodbye")
        if message == None:
            return
        # Let's regex and replace [[user]] [[atuser]] and [[server]]
        message = re.sub(self.regexUserName, "{}".format(DisplayName.name(member)), message)
        message = re.sub(self.regexUserPing, "{}".format(member.mention), message)
        message = re.sub(self.regexServer,   "{}".format(server.name), message)

        if suppress:
            message = Nullify.clean(message)
        if channel:
            await channel.send(message)
        else:
            await server.default_channel.send(message)

    @commands.command(pass_context=True)
    async def setwelcomechannel(self, ctx, *, channel : discord.TextChannel = None):
        """Sets the channel for the welcome and goodbye messages (bot-admin only)."""

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

        # Only allow admins to change server stats
        if not isAdmin:
            await ctx.channel.send('You do not have sufficient privileges to access this command.')
            return

        if channel == None:
            self.settings.setServerStat(ctx.message.guild, "WelcomeChannel", "")
            msg = 'Welcome and goodbye messages will be displayed in the default channel (**{}**).'.format(ctx.message.guild.default_channel.name)
            await ctx.channel.send(msg)
            return

        if type(channel) is str:
            try:
                role = discord.utils.get(message.guild.channels, name=role)
            except:
                print("That channel does not exist")
                return

        # If we made it this far - then we can add it
        self.settings.setServerStat(ctx.message.guild, "WelcomeChannel", channel.id)

        msg = 'Welcome and goodbye messages will be displayed in **{}**.'.format(channel.name)
        await ctx.channel.send(msg)

    @setwelcomechannel.error
    async def setwelcomechannel_error(self, ctx, error):
        # do stuff
        msg = 'setwelcomechannel Error: {}'.format(ctx)
        await error.channel.send(msg)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
    zine.i18n
    ~~~~~~~~~

    i18n tools for Zine.  This module provides various helpers for
    internationalization.  That is a translation system (with an API,
    compatible to standard gettext), timezone helpers as well as date
    parsing and formatting functions.

    General Architecture
    --------------------

    The i18n system is based on a few general principles.  Internally all
    times are stored in UTC as naive datetime objects (that means no tzinfo
    is present).  The internal language is American English and all text
    information is stored as unicode strings.

    For display strings are translated to the language of the blog and all
    dates as converted to the blog timezone.

    Translations are handled in a gettext inspired way via babel.  The
    translatable strings are stored in POT and PO files but the files
    Zine loads afterwards are stored in pickles rather than MO files.

    The reason for that is that we have to put additional information into
    these files.  Currently that is the information if strings are used on
    the client too.

    As a matter of fact we are using nearly nothing from the original gettext
    library that comes with Python.  Differences in the API are outlined in
    the `Translations` class docstring.

    Translation Workflow
    --------------------

    The extracting of strings (either for Zine core or plugins) is done
    with the `extract-messages` script.  If called without arguments it will
    extract the strings of the core, otherwise the strings of the plugin which
    is specified.  The messages collected are stored in the `messages.pot`
    file in the i18n folder of the core or plugin.

    The actual translations have to be updated by hand with those strings.
    The `update-translations` script will automatically add new strings to
    the po files and try to do fuzzy matching.

    To compile the translations into the pickled catalog files just use
    `compile-translations`.

    New languages are added with `add-translation`.

    :copyright: Copyright 2008 by Armin Ronacher.
    :license: BSD
"""
import os
import cPickle as pickle
from gettext import c2py
from datetime import datetime
from time import strptime
from weakref import WeakKeyDictionary

from babel import Locale, dates, UnknownLocaleError
from babel.support import Translations as TranslationsBase
from pytz import timezone, UTC
from werkzeug.exceptions import NotFound

import zine
from zine.environment import LOCALE_PATH
from zine.utils import dump_json

__all__ = ['_', 'gettext', 'ngettext', 'lazy_gettext', 'lazy_ngettext']

DATE_FORMATS = ['%m/%d/%Y', '%d/%m/%Y', '%Y%m%d', '%d. %m. %Y',
                '%m/%d/%y', '%d/%m/%y', '%d%m%y', '%m%d%y', '%y%m%d']
TIME_FORMATS = ['%H:%M', '%H:%M:%S', '%I:%M %p', '%I:%M:%S %p']

_js_translations = WeakKeyDictionary()

def load_translations(locale):
    """Load the translation for a locale.  If a locale does not exist
    the return value a fake translation object.  If the locale is unknown
    a `UnknownLocaleError` is raised.

    This only loads the translations for Zine itself and not the
    plugins.  Plugins themselves have an attribute translations that is
    the translations object pointing to the active translations of the
    plugin.

    The application code combines them into one translations object.
    """
    return Translations.load(LOCALE_PATH, locale)

class Translations(object):
    """A gettext like API for Zine."""

    def __init__(self, catalog=None, locale=None):
        if locale is not None:
            locale = Locale.parse(locale)
        self.locale = locale
        if catalog is None:
            self.messages = {}
            self.client_keys = set()
            self.plural_func = lambda n: int(n != 1)
            self.plural_expr = '(n != 1)'
        else:
            close = False
            if isinstance(catalog, basestring):
                catalog = file(catalog, 'rb')
                close = True
            try:
                dump = pickle.load(catalog)
                self.messages = dump['messages']
                self.client_keys = dump['client_keys']
                self.plural_func = c2py(dump['plural'])
                self.plural_expr = dump['plural']
            finally:
                if close:
                    catalog.close()
        self._lookup = self.messages.get

    def __nonzero__(self):
        return bool(self.messages)

    def gettext(self, string):
        msg = self._lookup(string)
        if msg is None:
            return unicode(string)
        elif msg.__class__ is tuple:
            return msg[0]
        return msg

    def ngettext(self, singular, plural, n):
        msgs = self._lookup(singular)
        if msgs is None or msgs.__class__ is not tuple:
            if n == 1:
                return unicode(singular)
            return unicode(plural)
        return msgs[self.plural_func(n)]

    # unicode aliases for gettext compatibility.  Jinja for example
    # will try to use those.
    ugettext = gettext
    ungettext = ngettext

    def merge(self, translations):
        """Update the translations with others."""
        self.messages.update(translations.messages)
        self.client_keys.update(translations.client_keys)

    @classmethod
    def load(cls, path, locale, domain='messages'):
        """Looks for .catalog files in the path provided in a gettext
        inspired manner.

        If there are no translations an empty locale is returned.
        """
        locale = Locale.parse(locale)
        catalog = os.path.join(path, str(locale), domain + '.catalog')
        if os.path.isfile(catalog):
            return Translations(catalog, locale)
        return Translations(locale=locale)

def gettext(string):
    """Translate the given string to the language of the application."""
    app = zine.application.get_application()
    if app is None:
        return unicode(string)
    return app.translations.gettext(string)

def ngettext(singular, plural, n):
    """Translate the possible pluralized string to the language of the
    application.
    """
    app = zine.application.get_application()
    if app is None:
        if n == 1:
            return unicode(singular)
        return unicode(plural)
    return app.translations.ngettext(singular, plural, n)

class _TranslationProxy(object):
    """Class for proxy strings from gettext translations.  This is a helper
    for the lazy_* functions from this module.

    The proxy implementation attempts to be as complete as possible, so that
    the lazy objects should mostly work as expected, for example for sorting.
    """
    __slots__ = ('_func', '_args')

    def __init__(self, func, *args):
        self._func = func
        self._args = args

    value = property(lambda x: object.__getattribute__(x, '_func')(*x._args))

    def __contains__(self, key):
        return key in self.value

    def __nonzero__(self):
        return bool(self.value)

    def __dir__(self):
        return dir(unicode)

    def __iter__(self):
        return iter(self.value)

    def __len__(self):
        return len(self.value)

    def __str__(self):
        return str(self.value)

    def __unicode__(self):
        return unicode(self.value)

    def __add__(self, other):
        return self.value + other

    def __radd__(self, other):
        return other + self.value

    def __mod__(self, other):
        return self.value % other

    def __rmod__(self, other):
        return other % self.value

    def __mul__(self, other):
        return self.value * other

    def __rmul__(self, other):
        return other * self.value

    def __lt__(self, other):
        return self.value < other

    def __le__(self, other):
        return self.value <= other

    def __eq__(self, other):
        return self.value == other

    def __ne__(self, other):
        return self.value != other

    def __gt__(self, other):
        return self.value > other

    def __ge__(self, other):
        return self.value >= other

    def __getattr__(self, name):
        if name == '__members__':
            return self.__dir__()
        return getattr(self.value, name)

    def __getstate__(self):
        return self._func, self._args

    def __setstate__(self, tup):
        self._func, self._args = tup

    def __getitem__(self, key):
        return self.value[key]

    def __repr__(self):
        try:
            return 'i' + repr(unicode(self.value))
        except:
            return '<%s broken>' % self.__class__.__name__

def lazy_gettext(string):
    """A lazy version of `gettext`."""
    if isinstance(string, _TranslationProxy):
        return string
    return _TranslationProxy(gettext, string)

def lazy_ngettext(singular, plural, n):
    """A lazy version of `ngettext`"""
    return _TranslationProxy(ngettext, singular, plural, n)

def per_language_string(en, **languages):
    """Returns a lazy string that returns the string for the current language
    of the application without looking up in the translations but the strings
    provided as keyword arguments.  The default language (english) can be
    specified as first argument.

    Here an example:

    >>> per_language_string('yes', de='ja', it='si')
    iu'yes'

    This should not be used for arbitrary translations but language depending
    strings such as configuration variables that do not represent text.

    An example could be quotes for different languages:

    >>> per_language_string(u'\u201c', de=u'\u201e')
    iu'\u201c'
    """
    def lookup():
        app = zine.application.get_application()
        lang = app and app.cfg['language'] or 'en'
        if lang in languages:
            return languages[lang]
        return languages['en']
    languages['en'] = en
    return _TranslationProxy(lookup)

def to_blog_timezone(datetime):
    """Convert a datetime object to the blog timezone."""
    if datetime.tzinfo is None:
        datetime = datetime.replace(tzinfo=UTC)
    tzinfo = get_timezone()
    return tzinfo.normalize(datetime.astimezone(tzinfo))

def to_utc(datetime):
    """Convert a datetime object to UTC and drop tzinfo."""
    if datetime.tzinfo is None:
        datetime = get_timezone().localize(datetime)
    return datetime.astimezone(UTC).replace(tzinfo=None)

def format_datetime(datetime=None, format='medium', rebase=True):
    """Return a date formatted according to the given pattern."""
    return _date_format(dates.format_datetime, datetime, format, rebase)

def format_system_datetime(datetime=None, rebase=True):
    """Formats a system datetime.  This is the format the admin
    panel uses by default.  (Format: YYYY-MM-DD hh:mm and in the
    user timezone unless rebase is disabled)
    """
    if rebase:
        datetime = to_blog_timezone(datetime)
    return u'%d-%02d-%02d %02d:%02d' % (
        datetime.year,
        datetime.month,
        datetime.day,
        datetime.hour,
        datetime.minute
    )

def format_date(date=None, format='medium', rebase=True):
    """Return the date formatted according to the pattern.  Rebasing only
    works for datetime objects passed to this function obviously.
    """
    if rebase and isinstance(date, datetime):
        date = to_blog_timezone(date)
    return _date_format(dates.format_date, date, format, rebase)

def format_month(date=None):
    """Format month and year of a date."""
    return format_date(date, 'MMMM YYYY')

def format_time(time=None, format='medium', rebase=True):
    """Return the time formatted according to the pattern."""
    return _date_format(dates.format_time, time, format, rebase)

def format_timedelta(datetime_or_timedelta, granularity='second'):
    """Format the elapsed time from the given date to now of the given
    timedelta.
    """
    if isinstance(datetime_or_timedelta, datetime):
        datetime_or_timedelta = datetime.utcnow() - datetime_or_timedelta
    return dates.format_timedelta(datetime_or_timedelta, granularity,
                                  locale=get_locale())

def list_timezones():
    """Return a list of all timezones."""
    from pytz import common_timezones
    # XXX: translate
    result = [(x, x.replace('_', ' ')) for x in common_timezones]
    result.sort(key=lambda x: x[1].lower())
    return result

def list_languages(self_translated=True):
    """Return a list of all languages."""
    if self_translated:
        locale = get_locale()
    else:
        locale = None

    languages = [('en', Locale('en').get_display_name())]

    for filename in os.listdir(LOCALE_PATH):
        if filename == 'en' or not \
           os.path.isfile(os.path.join(LOCALE_PATH, filename,
                                       'messages.catalog')):
            continue
        try:
            l = Locale.parse(filename)
        except UnknownLocaleError:
            continue
        languages.append((str(l), l.get_display_name()))

    languages.sort(key=lambda x: x[1].lower())
    return languages

def has_language(language):
    """Check if a language exists."""
    return language in dict(list_languages())

def has_timezone(tz):
    """When pased a timezone as string this function checks if
    the timezone is know.
    """
    try:
        timezone(tz)
    except:
        return False
    return True

def parse_datetime(string, rebase=True):
    """Parses a string into a datetime object.  Per default a conversion
    from the blog timezone to UTC is performed but returned as naive
    datetime object (that is tzinfo being None).  If rebasing is disabled
    the string is expected in UTC.

    The return value is **always** a naive datetime object in UTC.  This
    function should be considered of a lenient counterpart of
    `format_system_datetime`.
    """
    # shortcut: string as None or "now" or the current locale's
    # equivalent returns the current timestamp.
    if string is None or string.lower() in ('now', _('now')):
        return datetime.utcnow().replace(microsecond=0)

    def convert(format):
        """Helper that parses the string and convers the timezone."""
        rv = datetime(*strptime(string, format)[:7])
        if rebase:
            rv = to_utc(rv)
        return rv.replace(microsecond=0)
    cfg = zine.application.get_application().cfg

    # first of all try the following format because this is the format
    # Texpress will output by default for any date time string in the
    # administration panel.
    try:
        return convert(u'%Y-%m-%d %H:%M')
    except ValueError:
        pass

    # no go with time only, and current day
    for fmt in TIME_FORMATS:
        try:
            val = convert(fmt)
        except ValueError:
            continue
        return to_utc(datetime.utcnow().replace(hour=val.hour,
                      minute=val.minute, second=val.second, microsecond=0))

    # no try various types of date + time strings
    def combined():
        for t_fmt in TIME_FORMATS:
            for d_fmt in DATE_FORMATS:
                yield t_fmt + ' ' + d_fmt
                yield d_fmt + ' ' + t_fmt

    for fmt in combined():
        try:
            return convert(fmt)
        except ValueError:
            pass

    raise ValueError('invalid date format')

def _date_format(formatter, obj, format, rebase, **extra):
    """Internal helper that formats the date."""
    locale = get_locale()
    extra = {}
    if formatter is not dates.format_date and rebase:
        extra['tzinfo'] = get_timezone()
    return formatter(obj, format, locale=locale, **extra)

def get_timezone(name=None):
    """Return the timezone for the given identifier or the timezone
    of the application based on the configuration.
    """
    if name is None:
        name = zine.application.get_application().cfg['timezone']
    return timezone(name)

def get_locale():
    """Return the current locale."""
    app = zine.application.get_application()
    if app is None:
        return Locale('en')
    return app.locale

def serve_javascript(request):
    """Serves the JavaScript translations."""
    code = _js_translations.get(request.app)
    if code is None:
        t = request.app.translations
        code = 'Zine.addTranslations(%s)' % dump_json(dict(
            messages=dict((k, t.messages[k]) for k in t.client_keys),
            plural_expr=t.plural_expr,
            locale=str(t.locale)
        ))
        _js_translations[request.app] = code
    response = zine.application.Response(code, mimetype='application/javascript')
    response.add_etag()
    response.make_conditional(request)
    return response

_ = gettext
<commit_msg>Automated merge with ssh://team@pocoo.org/zine-main<commit_after># -*- coding: utf-8 -*-
"""
    zine.i18n
    ~~~~~~~~~

    i18n tools for Zine.  This module provides various helpers for
    internationalization.  That is a translation system (with an API,
    compatible to standard gettext), timezone helpers as well as date
    parsing and formatting functions.

    General Architecture
    --------------------

    The i18n system is based on a few general principles.  Internally all
    times are stored in UTC as naive datetime objects (that means no tzinfo
    is present).  The internal language is American English and all text
    information is stored as unicode strings.

    For display strings are translated to the language of the blog and all
    dates as converted to the blog timezone.

    Translations are handled in a gettext inspired way via babel.  The
    translatable strings are stored in POT and PO files but the files
    Zine loads afterwards are stored in pickles rather than MO files.

    The reason for that is that we have to put additional information into
    these files.  Currently that is the information if strings are used on
    the client too.

    As a matter of fact we are using nearly nothing from the original gettext
    library that comes with Python.  Differences in the API are outlined in
    the `Translations` class docstring.

    Translation Workflow
    --------------------

    The extracting of strings (either for Zine core or plugins) is done
    with the `extract-messages` script.  If called without arguments it will
    extract the strings of the core, otherwise the strings of the plugin which
    is specified.  The messages collected are stored in the `messages.pot`
    file in the i18n folder of the core or plugin.

    The actual translations have to be updated by hand with those strings.
    The `update-translations` script will automatically add new strings to
    the po files and try to do fuzzy matching.

    To compile the translations into the pickled catalog files just use
    `compile-translations`.

    New languages are added with `add-translation`.

    :copyright: Copyright 2008 by Armin Ronacher.
    :license: BSD
"""
import os
import cPickle as pickle
from gettext import c2py
from datetime import datetime
from time import strptime
from weakref import WeakKeyDictionary

from babel import Locale, dates, UnknownLocaleError
from babel.support import Translations as TranslationsBase
from pytz import timezone, UTC
from werkzeug.exceptions import NotFound

import zine
from zine.environment import LOCALE_PATH
from zine.utils import dump_json

__all__ = ['_', 'gettext', 'ngettext', 'lazy_gettext', 'lazy_ngettext']

DATE_FORMATS = ['%m/%d/%Y', '%d/%m/%Y', '%Y%m%d', '%d. %m. %Y',
                '%m/%d/%y', '%d/%m/%y', '%d%m%y', '%m%d%y', '%y%m%d']
TIME_FORMATS = ['%H:%M', '%H:%M:%S', '%I:%M %p', '%I:%M:%S %p']

_js_translations = WeakKeyDictionary()

def load_translations(locale):
    """Load the translation for a locale.  If a locale does not exist
    the return value a fake translation object.  If the locale is unknown
    a `UnknownLocaleError` is raised.

    This only loads the translations for Zine itself and not the
    plugins.  Plugins themselves have an attribute translations that is
    the translations object pointing to the active translations of the
    plugin.

    The application code combines them into one translations object.
    """
    return Translations.load(LOCALE_PATH, locale)

class Translations(object):
    """A gettext like API for Zine."""

    def __init__(self, catalog=None, locale=None):
        if locale is not None:
            locale = Locale.parse(locale)
        self.locale = locale
        if catalog is None:
            self.messages = {}
            self.client_keys = set()
            self.plural_func = lambda n: int(n != 1)
            self.plural_expr = '(n != 1)'
        else:
            close = False
            if isinstance(catalog, basestring):
                catalog = file(catalog, 'rb')
                close = True
            try:
                dump = pickle.load(catalog)
                self.messages = dump['messages']
                self.client_keys = dump['client_keys']
                self.plural_func = c2py(dump['plural'])
                self.plural_expr = dump['plural']
            finally:
                if close:
                    catalog.close()
        self._lookup = self.messages.get

    def __nonzero__(self):
        return bool(self.messages)

    def gettext(self, string):
        msg = self._lookup(string)
        if msg is None:
            return unicode(string)
        elif msg.__class__ is tuple:
            return msg[0]
        return msg

    def ngettext(self, singular, plural, n):
        msgs = self._lookup(singular)
        if msgs is None or msgs.__class__ is not tuple:
            if n == 1:
                return unicode(singular)
            return unicode(plural)
        return msgs[self.plural_func(n)]

    # unicode aliases for gettext compatibility.  Jinja for example
    # will try to use those.
    ugettext = gettext
    ungettext = ngettext

    def merge(self, translations):
        """Update the translations with others."""
        self.messages.update(translations.messages)
        self.client_keys.update(translations.client_keys)

    @classmethod
    def load(cls, path, locale, domain='messages'):
        """Looks for .catalog files in the path provided in a gettext
        inspired manner.

        If there are no translations an empty locale is returned.
        """
        locale = Locale.parse(locale)
        catalog = os.path.join(path, str(locale), domain + '.catalog')
        if os.path.isfile(catalog):
            return Translations(catalog, locale)
        return Translations(locale=locale)

def gettext(string):
    """Translate type(string)iven string to the language of the application."""
    app = zine.application.get_application()
    if app is None:
        return unicode(string)
    return app.translations.gettext(string)

def ngettext(singular, plural, n):
    """Translate the possible pluralized string to the language of the
    application.
    """
    app = zine.application.get_application()
    if app is None:
        if n == 1:
            return unicode(singular)
        return unicode(plural)
    return app.translations.ngettext(singular, plural, n)

class _TranslationProxy(object):
    """Class for proxy strings from gettext translations.  This is a helper
    for the lazy_* functions from this module.

    The proxy implementation attempts to be as complete as possible, so that
    the lazy objects should mostly work as expected, for example for sorting.
    """
    __slots__ = ('_func', '_args')

    def __init__(self, func, *args):
        self._func = func
        self._args = args

    value = property(lambda x: x._func(*x._args))

    def __contains__(self, key):
        return key in self.value

    def __nonzero__(self):
        return bool(self.value)

    def __dir__(self):
        return dir(unicode)

    def __iter__(self):
        return iter(self.value)

    def __len__(self):
        return len(self.value)

    def __str__(self):
        return str(self.value)

    def __unicode__(self):
        return unicode(self.value)

    def __add__(self, other):
        return self.value + other

    def __radd__(self, other):
        return other + self.value

    def __mod__(self, other):
        return self.value % other

    def __rmod__(self, other):
        return other % self.value

    def __mul__(self, other):
        return self.value * other

    def __rmul__(self, other):
        return other * self.value

    def __lt__(self, other):
        return self.value < other

    def __le__(self, other):
        return self.value <= other

    def __eq__(self, other):
        return self.value == other

    def __ne__(self, other):
        return self.value != other

    def __gt__(self, other):
        return self.value > other

    def __ge__(self, other):
        return self.value >= other

    def __getattr__(self, name):
        if name == '__members__':
            return self.__dir__()
        return getattr(self.value, name)

    def __getstate__(self):
        return self._func, self._args

    def __setstate__(self, tup):
        self._func, self._args = tup

    def __getitem__(self, key):
        return self.value[key]

    def __copy__(self):
        return self

    def __repr__(self):
        try:
            return 'i' + repr(unicode(self.value))
        except:
            return '<%s broken>' % self.__class__.__name__

def lazy_gettext(string):
    """A lazy version of `gettext`."""
    if isinstance(string, _TranslationProxy):
        return string
    return _TranslationProxy(gettext, string)

def lazy_ngettext(singular, plural, n):
    """A lazy version of `ngettext`"""
    return _TranslationProxy(ngettext, singular, plural, n)

def per_language_string(en, **languages):
    """Returns a lazy string that returns the string for the current language
    of the application without looking up in the translations but the strings
    provided as keyword arguments.  The default language (english) can be
    specified as first argument.

    Here an example:

    >>> per_language_string('yes', de='ja', it='si')
    iu'yes'

    This should not be used for arbitrary translations but language depending
    strings such as configuration variables that do not represent text.

    An example could be quotes for different languages:

    >>> per_language_string(u'\u201c', de=u'\u201e')
    iu'\u201c'
    """
    def lookup():
        app = zine.application.get_application()
        lang = app and app.cfg['language'] or 'en'
        if lang in languages:
            return languages[lang]
        return languages['en']
    languages['en'] = en
    return _TranslationProxy(lookup)

def to_blog_timezone(datetime):
    """Convert a datetime object to the blog timezone."""
    if datetime.tzinfo is None:
        datetime = datetime.replace(tzinfo=UTC)
    tzinfo = get_timezone()
    return tzinfo.normalize(datetime.astimezone(tzinfo))

def to_utc(datetime):
    """Convert a datetime object to UTC and drop tzinfo."""
    if datetime.tzinfo is None:
        datetime = get_timezone().localize(datetime)
    return datetime.astimezone(UTC).replace(tzinfo=None)

def format_datetime(datetime=None, format='medium', rebase=True):
    """Return a date formatted according to the given pattern."""
    return _date_format(dates.format_datetime, datetime, format, rebase)

def format_system_datetime(datetime=None, rebase=True):
    """Formats a system datetime.  This is the format the admin
    panel uses by default.  (Format: YYYY-MM-DD hh:mm and in the
    user timezone unless rebase is disabled)
    """
    if rebase:
        datetime = to_blog_timezone(datetime)
    return u'%d-%02d-%02d %02d:%02d' % (
        datetime.year,
        datetime.month,
        datetime.day,
        datetime.hour,
        datetime.minute
    )

def format_date(date=None, format='medium', rebase=True):
    """Return the date formatted according to the pattern.  Rebasing only
    works for datetime objects passed to this function obviously.
    """
    if rebase and isinstance(date, datetime):
        date = to_blog_timezone(date)
    return _date_format(dates.format_date, date, format, rebase)

def format_month(date=None):
    """Format month and year of a date."""
    return format_date(date, 'MMMM YYYY')

def format_time(time=None, format='medium', rebase=True):
    """Return the time formatted according to the pattern."""
    return _date_format(dates.format_time, time, format, rebase)

def format_timedelta(datetime_or_timedelta, granularity='second'):
    """Format the elapsed time from the given date to now of the given
    timedelta.
    """
    if isinstance(datetime_or_timedelta, datetime):
        datetime_or_timedelta = datetime.utcnow() - datetime_or_timedelta
    return dates.format_timedelta(datetime_or_timedelta, granularity,
                                  locale=get_locale())

def list_timezones():
    """Return a list of all timezones."""
    from pytz import common_timezones
    # XXX: translate
    result = [(x, x.replace('_', ' ')) for x in common_timezones]
    result.sort(key=lambda x: x[1].lower())
    return result

def list_languages(self_translated=True):
    """Return a list of all languages."""
    if self_translated:
        locale = get_locale()
    else:
        locale = None

    languages = [('en', Locale('en').get_display_name())]

    for filename in os.listdir(LOCALE_PATH):
        if filename == 'en' or not \
           os.path.isfile(os.path.join(LOCALE_PATH, filename,
                                       'messages.catalog')):
            continue
        try:
            l = Locale.parse(filename)
        except UnknownLocaleError:
            continue
        languages.append((str(l), l.get_display_name()))

    languages.sort(key=lambda x: x[1].lower())
    return languages

def has_language(language):
    """Check if a language exists."""
    return language in dict(list_languages())

def has_timezone(tz):
    """When pased a timezone as string this function checks if
    the timezone is know.
    """
    try:
        timezone(tz)
    except:
        return False
    return True

def parse_datetime(string, rebase=True):
    """Parses a string into a datetime object.  Per default a conversion
    from the blog timezone to UTC is performed but returned as naive
    datetime object (that is tzinfo being None).  If rebasing is disabled
    the string is expected in UTC.

    The return value is **always** a naive datetime object in UTC.  This
    function should be considered of a lenient counterpart of
    `format_system_datetime`.
    """
    # shortcut: string as None or "now" or the current locale's
    # equivalent returns the current timestamp.
    if string is None or string.lower() in ('now', _('now')):
        return datetime.utcnow().replace(microsecond=0)

    def convert(format):
        """Helper that parses the string and convers the timezone."""
        rv = datetime(*strptime(string, format)[:7])
        if rebase:
            rv = to_utc(rv)
        return rv.replace(microsecond=0)
    cfg = zine.application.get_application().cfg

    # first of all try the following format because this is the format
    # Texpress will output by default for any date time string in the
    # administration panel.
    try:
        return convert(u'%Y-%m-%d %H:%M')
    except ValueError:
        pass

    # no go with time only, and current day
    for fmt in TIME_FORMATS:
        try:
            val = convert(fmt)
        except ValueError:
            continue
        return to_utc(datetime.utcnow().replace(hour=val.hour,
                      minute=val.minute, second=val.second, microsecond=0))

    # no try various types of date + time strings
    def combined():
        for t_fmt in TIME_FORMATS:
            for d_fmt in DATE_FORMATS:
                yield t_fmt + ' ' + d_fmt
                yield d_fmt + ' ' + t_fmt

    for fmt in combined():
        try:
            return convert(fmt)
        except ValueError:
            pass

    raise ValueError('invalid date format')

def _date_format(formatter, obj, format, rebase, **extra):
    """Internal helper that formats the date."""
    locale = get_locale()
    extra = {}
    if formatter is not dates.format_date and rebase:
        extra['tzinfo'] = get_timezone()
    return formatter(obj, format, locale=locale, **extra)

def get_timezone(name=None):
    """Return the timezone for the given identifier or the timezone
    of the application based on the configuration.
    """
    if name is None:
        name = zine.application.get_application().cfg['timezone']
    return timezone(name)

def get_locale():
    """Return the current locale."""
    app = zine.application.get_application()
    if app is None:
        return Locale('en')
    return app.locale

def serve_javascript(request):
    """Serves the JavaScript translations."""
    code = _js_translations.get(request.app)
    if code is None:
        t = request.app.translations
        code = 'Zine.addTranslations(%s)' % dump_json(dict(
            messages=dict((k, t.messages[k]) for k in t.client_keys),
            plural_expr=t.plural_expr,
            locale=str(t.locale)
        ))
        _js_translations[request.app] = code
    response = zine.application.Response(code, mimetype='application/javascript')
    response.add_etag()
    response.make_conditional(request)
    return response

_ = gettext
<|endoftext|>


<commit_before>'''create ColumnsTable that can be printed in a txt file

    c = ColumnsTable(('colname', 6, '%6.2f', ('header1', 'header2'), 'legend'),)
    c.append(colname=10)  # also other column name=value pairs
    ...
    c.append_legend()
    r = Report()
    r.append('title')
    c.iterate_lines(lambda line: r.append(line))  # also takes kwds
    r.write(path)
'''

import collections
import pdb
import unittest

ColumnsTableFields = collections.namedtuple('ColumnsTableFields', 'name width formatter headers legend')

class ColumnsTable(object):
    def __init__(self, column_defs, verbose=False):
        'column_defs is an iterable with elements (name, width, formatter, (header-list), legend)'
        self._column_defs = []
        self._number_of_header_lines = 0
        for column_def in column_defs:
            cd3 = column_def[3]
            # convert 'abc' to ('abc',)
            headers = cd3 if isinstance(cd3, collections.Iterable) else (cd3,)
            if self._number_of_header_lines != 0:
                if self._number_of_header_lines != len(headers):
                    print 'inconsistent number of lines in header'
                    print 'have both %d and %d' % (self._number_of_header_lines, len(headers))
                    print 'found at headesr: %s' % headers
                    pdb.set_trace()
            self._number_of_header_lines = len(headers)
            self._column_defs.append(ColumnsTableFields(column_def[0],
                                                        column_def[1],
                                                        column_def[2],
                                                        headers,
                                                        column_def[4]))
        self._lines = []
        self._verbose = verbose
        self._header()

    def append_legend_lines(self):
        def cat_headers(headers):
            text = ''
            for header in headers:
                if len(text) > 0:
                    text += ' '
                text += header.strip()
            return text

        self._append_line(' ')
        self._append_line('column legend:')
        for cd in self._column_defs:
            line = '%12s -> %s' % (cat_headers(cd.headers), cd.legend)
            self._append_line(line)

    def iterlines(self):
        for line in self._lines:
            yield line

    def append_detail_line(self, **kwds):
        line = ''
        for cd in self._column_defs:
            if cd.name in kwds:
                glyph = cd.formatter % kwds[cd.name]
            else:
                glyph = ' ' * cd.width
            if len(line) > 0:
                line += ' '
            line += glyph
        self._append_line(line)

    def _append_line(self, line):
        if self._verbose:
            print line
        self._lines.append(line)

    def _header(self):
        def append_header(index):
            line = ''
            for cd in self._column_defs:
                formatter = '%' + str(cd.width) + 's'
                formatted = formatter % cd.headers[index]
                line += (' ' if len(line) > 0 else '') + formatted
            self._append_line(line)

        for header_line_index in xrange(self._number_of_header_lines):
            append_header(header_line_index)

    def _print(self):
        for line in self._lines:
            print line

class TestColumnsTable(unittest.TestCase):
    def setUp(self,):
        self.verbose = False
        self.columns = ColumnsTable(
            (('a', 3, '%3d', ('one', 'num'), 'angstroms'),
             ('bcd', 10, '%10.2f', ('length', 'meters'), 'something really big'),
             ),
            verbose=self.verbose,
        )

    def test_construction(self):
        self.assertTrue(True)
        pass  # tested in setUp()

    def test_append_detail_line(self):
        c = self.columns
        c.append_detail_line(a=10, bcd=20)
        c.append_detail_line(bcd=30)
        c.append_detail_line(a=50)
        self.assertEqual(len(c._lines), 5)
        self.assertTrue(True)

    def test_iterlines(self):
        c = self.columns
        counter = collections.Counter()

        for line in c.iterlines():
            counter['line'] += 1
            if self.verbose:
                print line

        self.assertEqual(counter['line'], 2)  # only 2 column headers lines

    def test_append_legend_lines(self):
        c = self.columns
        c.append_legend_lines()
        self.assertTrue(True)

if __name__ == '__main__':
    unittest.main()
    if False:
        pdb
<commit_msg>change API (mimic Report)<commit_after>'''create ColumnsTable that can be printed in a txt file

    c = ColumnsTable(('colname', 6, '%6.2f', ('header1', 'header2'), 'legend'),)
    c.append(colname=10)  # also other column name=value pairs
    ...
    c.append_legend()
    r = Report()
    r.append('title')
    c.iterate_lines(lambda line: r.append(line))  # also takes kwds
    r.write(path)
'''

import collections
import pdb
import unittest

ColumnsTableFields = collections.namedtuple('ColumnsTableFields', 'name width formatter headers legend')

class ColumnsTable(object):
    def __init__(self, column_defs, verbose=False):
        'column_defs is an iterable with elements (name, width, formatter, (header-list), legend)'
        self._column_defs = []
        self._number_of_header_lines = 0
        for column_def in column_defs:
            cd3 = column_def[3]
            # convert 'abc' to ('abc',)
            headers = cd3 if isinstance(cd3, collections.Iterable) else (cd3,)
            if self._number_of_header_lines != 0:
                if self._number_of_header_lines != len(headers):
                    print 'inconsistent number of lines in header'
                    print 'have both %d and %d' % (self._number_of_header_lines, len(headers))
                    print 'found at headesr: %s' % headers
                    pdb.set_trace()
            self._number_of_header_lines = len(headers)
            self._column_defs.append(ColumnsTableFields(column_def[0],
                                                        column_def[1],
                                                        column_def[2],
                                                        headers,
                                                        column_def[4]))
        self._lines = []
        self._verbose = verbose
        self._header()

    def append_legend(self):
        def cat_headers(headers):
            text = ''
            for header in headers:
                if len(text) > 0:
                    text += ' '
                text += header.strip()
            return text

        self._append_line(' ')
        self._append_line('column legend:')
        for cd in self._column_defs:
            line = '%12s -> %s' % (cat_headers(cd.headers), cd.legend)
            self._append_line(line)

    def iterlines(self):
        for line in self._lines:
            yield line

    def append_detail(self, **kwds):
        line = ''
        for cd in self._column_defs:
            if cd.name in kwds:
                glyph = cd.formatter % kwds[cd.name]
            else:
                glyph = ' ' * cd.width
            if len(line) > 0:
                line += ' '
            line += glyph
        self._append_line(line)

    def _append_line(self, line):
        if self._verbose:
            print line
        self._lines.append(line)

    def _header(self):
        def append_header(index):
            line = ''
            for cd in self._column_defs:
                formatter = '%' + str(cd.width) + 's'
                formatted = formatter % cd.headers[index]
                line += (' ' if len(line) > 0 else '') + formatted
            self._append_line(line)

        for header_line_index in xrange(self._number_of_header_lines):
            append_header(header_line_index)

    def _print(self):
        for line in self._lines:
            print line

class TestColumnsTable(unittest.TestCase):
    def setUp(self,):
        self.verbose = False
        self.columns = ColumnsTable(
            (('a', 3, '%3d', ('one', 'num'), 'angstroms'),
             ('bcd', 10, '%10.2f', ('length', 'meters'), 'something really big'),
             ),
            verbose=self.verbose,
        )

    def test_construction(self):
        self.assertTrue(True)
        pass  # tested in setUp()

    def test_append_detail_line(self):
        c = self.columns
        c.append_detail(a=10, bcd=20)
        c.append_detail(bcd=30)
        c.append_detail(a=50)
        self.assertEqual(len(c._lines), 5)
        self.assertTrue(True)

    def test_iterlines(self):
        c = self.columns
        counter = collections.Counter()

        for line in c.iterlines():
            counter['line'] += 1
            if self.verbose:
                print line

        self.assertEqual(counter['line'], 2)  # only 2 column headers lines

    def test_append_legend_lines(self):
        c = self.columns
        c.append_legend()
        self.assertTrue(True)

if __name__ == '__main__':
    unittest.main()
    if False:
        pdb
<|endoftext|>


<commit_before>from .experiment_server import app
from .experiment_server import WAITING_ROOM_CHANNEL
from ..heroku.worker import conn
from flask_sockets import Sockets
from redis import ConnectionError
import gevent

sockets = Sockets(app)

CHANNELS = [
    WAITING_ROOM_CHANNEL
]

class ChatBackend(object):
    """Chat backend which relays messages from a redis pubsub to clients.

    This is run by each web process; all processes receive the messages.

    Inspired by https://devcenter.heroku.com/articles/python-websockets
    """

    def __init__(self):
        self.pubsub = conn.pubsub()
        try:
            self.pubsub.subscribe(CHANNELS)
            app.logger.debug('Subscribed to channels: {}'.format(CHANNELS))
        except ConnectionError:
            app.logger.exception('Could not connect to redis.')

        self.clients = {}
        for channel in CHANNELS:
            self.clients[channel] = []

    def subscribe(self, client, channel=None):
        """Register a new client to receive messages."""
        if channel is not None:
            if channel not in self.clients:
                raise ValueError('Unknown channel: {}'.format(channel))
            self.clients[channel].append(client)
        else:
            for channel in CHANNELS:
                self.clients[channel].append(client)

    def unsubscribe(self, client, channel):
        if client in self.clients[channel]:
            self.clients[channel].remove(client)

    def send(self, client, data):
        """Send data to one client.

        Automatically discards invalid connections.
        """
        try:
            client.send(data)
        except:
            for channel in self.clients:
                self.unsubscribe(client, channel)

    def run(self):
        """Listens for new messages in redis, and sends them to clients."""
        for message in self.pubsub.listen():
            data = message.get('data')
            if message['type'] == 'message':
                channel = message['channel']
                count = len(self.clients[channel])
                if count:
                    app.logger.debug(
                        'Relaying message on channel {} to {} clients: {}'.format(
                            channel, len(self.clients[channel]), data))
                    for client in self.clients[channel]:
                        gevent.spawn(
                            self.send, client, '{}:{}'.format(channel, data))

    def start(self):
        """Starts listening in the background."""
        self.greenlet = gevent.spawn(self.run)

    def stop(self):
        self.greenlet.kill()

chat_backend = ChatBackend()
app.before_first_request(chat_backend.start)

@sockets.route('/receive_chat')
def outbox(ws):
    chat_backend.subscribe(ws)

    while not ws.closed:
        # Wait for chat backend
        gevent.sleep(0.1)
<commit_msg>Catch specific exception<commit_after>from .experiment_server import app
from .experiment_server import WAITING_ROOM_CHANNEL
from ..heroku.worker import conn
from flask_sockets import Sockets
from redis import ConnectionError
import gevent
import socket

sockets = Sockets(app)

CHANNELS = [
    WAITING_ROOM_CHANNEL
]

class ChatBackend(object):
    """Chat backend which relays messages from a redis pubsub to clients.

    This is run by each web process; all processes receive the messages.

    Inspired by https://devcenter.heroku.com/articles/python-websockets
    """

    def __init__(self):
        self.pubsub = conn.pubsub()
        try:
            self.pubsub.subscribe(CHANNELS)
            app.logger.debug('Subscribed to channels: {}'.format(CHANNELS))
        except ConnectionError:
            app.logger.exception('Could not connect to redis.')

        self.clients = {}
        for channel in CHANNELS:
            self.clients[channel] = []

    def subscribe(self, client, channel=None):
        """Register a new client to receive messages."""
        if channel is not None:
            if channel not in self.clients:
                raise ValueError('Unknown channel: {}'.format(channel))
            self.clients[channel].append(client)
        else:
            for channel in CHANNELS:
                self.clients[channel].append(client)

    def unsubscribe(self, client, channel):
        if client in self.clients[channel]:
            self.clients[channel].remove(client)

    def send(self, client, data):
        """Send data to one client.

        Automatically discards invalid connections.
        """
        try:
            client.send(data)
        except socket.error:
            for channel in self.clients:
                self.unsubscribe(client, channel)

    def run(self):
        """Listens for new messages in redis, and sends them to clients."""
        for message in self.pubsub.listen():
            data = message.get('data')
            if message['type'] == 'message':
                channel = message['channel']
                count = len(self.clients[channel])
                if count:
                    app.logger.debug(
                        'Relaying message on channel {} to {} clients: {}'.format(
                            channel, len(self.clients[channel]), data))
                    for client in self.clients[channel]:
                        gevent.spawn(
                            self.send, client, '{}:{}'.format(channel, data))

    def start(self):
        """Starts listening in the background."""
        self.greenlet = gevent.spawn(self.run)

    def stop(self):
        self.greenlet.kill()

chat_backend = ChatBackend()
app.before_first_request(chat_backend.start)

@sockets.route('/receive_chat')
def outbox(ws):
    chat_backend.subscribe(ws)

    while not ws.closed:
        # Wait for chat backend
        gevent.sleep(0.1)
<|endoftext|>


<commit_before>import subprocess
import os
import importlib
from time import sleep
from sys import platform as _platform

currentDir = os.path.dirname(os.path.abspath(__file__))

def callPlugin(command,config):
	sequence = command.split()[1::]
	command = command.split()[0]

	for plugin in plugins:
		if plugin["command"] == command:
			command = plugin["callback"]
			command(pyTerm=config,sequence=sequence)
			return True
	return False

def importPlugins():
	dirs = os.listdir( currentDir + "/plugins")
	pluginData = []
	pluginCommands = " "
	for file in dirs:
		if not "pyc" in file.split(".") and not "__init__" in file.split("."):
			moduleName = os.path.splitext(file)[0]
			module = importlib.import_module("plugins." + moduleName)
			moduleClassPrototype = getattr(module,moduleName)
			moduleLoadedClass = moduleClassPrototype()
			initiator = getattr(moduleLoadedClass,"__pytermconfig__")
			data = initiator()
			pluginData.append(initiator())
			pluginCommands += data["command"] + " "

	plugins = pluginData
	return pluginCommands

def load_commands():
	if _platform == "linux" or _platform == "linux2":
		filename = currentDir + '/listCommands.sh'
	elif _platform == "darwin":
		filename = currentDir + '/mac_commands.sh'
	else:
		print "Your system is not supported"
		exit()

	command = subprocess.Popen(['bash',filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
	commands, err = command.communicate()
	commands = commands.replace("ls","")
	commands += " PyTerm"
	commands += importPlugins()
	return commands.split()

def cache_commands(commands):
	commandsString = " ".join(commands)
	cachedCommands = open(currentDir + "/config/commands", 'w')
	cachedCommands.write(commandsString)

def load():
	if os.path.isfile("config/commands"):
		cachedCommands = open(currentDir + "/config/commands", 'r').read()
		return cachedCommands.split()
	else:
		commands = load_commands()
		cache_commands(commands)
		return commands

def find(query):
	for command in available:
		if command == query:
			return True

	return False

def run(commandString,config):
	commandLine = commandString.split()

	if commandString == "{{BREAKAPPLICATION}}":
		return False

	if not commandString == "{{BREAKAPPLICATION}}" and len(commandLine) > 0 and not commandLine[0] == "ls":

		try:

			print ""
			test = subprocess.Popen(commandLine, stdout=subprocess.PIPE)
			output = test.communicate()[0]
			print output
		except OSError as e:
			if not callPlugin(commandString,config):
				print e,

	if commandLine[0] == "ls":
		callPlugin(commandString,config)

	print ""

	return True

available = load()
<commit_msg>close the files once read/write is complete<commit_after>import subprocess
import os
import importlib
from time import sleep
from sys import platform as _platform

currentDir = os.path.dirname(os.path.abspath(__file__))

def callPlugin(command,config):
	sequence = command.split()[1::]
	command = command.split()[0]

	for plugin in plugins:
		if plugin["command"] == command:
			command = plugin["callback"]
			command(pyTerm=config,sequence=sequence)
			return True
	return False

def importPlugins():
	dirs = os.listdir( currentDir + "/plugins")
	pluginData = []
	pluginCommands = " "
	for file in dirs:
		if not "pyc" in file.split(".") and not "__init__" in file.split("."):
			moduleName = os.path.splitext(file)[0]
			module = importlib.import_module("plugins." + moduleName)
			moduleClassPrototype = getattr(module,moduleName)
			moduleLoadedClass = moduleClassPrototype()
			initiator = getattr(moduleLoadedClass,"__pytermconfig__")
			data = initiator()
			pluginData.append(initiator())
			pluginCommands += data["command"] + " "

	plugins = pluginData
	return pluginCommands

def load_commands():
	if _platform == "linux" or _platform == "linux2":
		filename = currentDir + '/listCommands.sh'
	elif _platform == "darwin":
		filename = currentDir + '/mac_commands.sh'
	else:
		print "Your system is not supported"
		exit()

	command = subprocess.Popen(['bash',filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
	commands, err = command.communicate()
	commands = commands.replace("ls","")
	commands += " PyTerm"
	commands += importPlugins()
	return commands.split()

def cache_commands(commands):
	commandsString = " ".join(commands)
	with open(os.path.join(currentDir, "/config/commands"), 'w') as cC:
		cC.write(commandsString)

def load():
	if os.path.isfile("config/commands"):
		with open(os.path.join(currentDir, "/config/commands"), 'r') as cC:
			cachedCommands = cC.read()
		return cachedCommands.split()
	else:
		commands = load_commands()
		cache_commands(commands)
		return commands

def find(query):
	for command in available:
		if command == query:
			return True

	return False

def run(commandString,config):
	commandLine = commandString.split()

	if commandString == "{{BREAKAPPLICATION}}":
		return False

	if not commandString == "{{BREAKAPPLICATION}}" and len(commandLine) > 0 and not commandLine[0] == "ls":

		try:

			print ""
			test = subprocess.Popen(commandLine, stdout=subprocess.PIPE)
			output = test.communicate()[0]
			print output
		except OSError as e:
			if not callPlugin(commandString,config):
				print e,

	if commandLine[0] == "ls":
		callPlugin(commandString,config)

	print ""

	return True

available = load()
<|endoftext|>