<commit_before># -*- coding: utf-8 -*-
"""
    pyspecific.py
    ~~~~~~~~~~~~~

    Sphinx extension with Python doc-specific markup.

    :copyright: 2008, 2009, 2010 by Georg Brandl.
    :license: Python license.
"""

ISSUE_URI = 'http://bugs.python.org/issue%s'
SOURCE_URI = 'http://svn.python.org/view/python/branches/py3k/%s?view=markup'

from docutils import nodes, utils
from sphinx.util.nodes import split_explicit_title

# monkey-patch reST parser to disable alphabetic and roman enumerated lists
from docutils.parsers.rst.states import Body
Body.enum.converters['loweralpha'] = \
    Body.enum.converters['upperalpha'] = \
    Body.enum.converters['lowerroman'] = \
    Body.enum.converters['upperroman'] = lambda x: None

# monkey-patch HTML translator to give versionmodified paragraphs a class
def new_visit_versionmodified(self, node):
    self.body.append(self.starttag(node, 'p', CLASS=node['type']))
    text = versionlabels[node['type']] % node['version']
    if len(node):
        text += ': '
    else:
        text += '.'
    self.body.append('%s' % text)

from sphinx.writers.html import HTMLTranslator
from sphinx.locale import versionlabels
HTMLTranslator.visit_versionmodified = new_visit_versionmodified

# Support for marking up and linking to bugs.python.org issues

def issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
    issue = utils.unescape(text)
    text = 'issue ' + issue
    refnode = nodes.reference(text, text, refuri=ISSUE_URI % issue)
    return [refnode], []

# Support for linking to Python source files easily

def source_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
    has_t, title, target = split_explicit_title(text)
    title = utils.unescape(title)
    target = utils.unescape(target)
    refnode = nodes.reference(title, title, refuri=SOURCE_URI % target)
    return [refnode], []

# Support for marking up implementation details

from sphinx.util.compat import Directive

class ImplementationDetail(Directive):

    has_content = True
    required_arguments = 0
    optional_arguments = 1
    final_argument_whitespace = True

    def run(self):
        pnode = nodes.compound(classes=['impl-detail'])
        content = self.content
        add_text = nodes.strong('CPython implementation detail:',
                                'CPython implementation detail:')
        if self.arguments:
            n, m = self.state.inline_text(self.arguments[0], self.lineno)
            pnode.append(nodes.paragraph('', '', *(n + m)))
        self.state.nested_parse(content, self.content_offset, pnode)
        if pnode.children and isinstance(pnode[0], nodes.paragraph):
            pnode[0].insert(0, add_text)
            pnode[0].insert(1, nodes.Text(' '))
        else:
            pnode.insert(0, nodes.paragraph('', '', add_text))
        return [pnode]

# Support for documenting decorators

from sphinx import addnodes
from sphinx.domains.python import PyModulelevel, PyClassmember

class PyDecoratorMixin(object):
    def handle_signature(self, sig, signode):
        ret = super(PyDecoratorMixin, self).handle_signature(sig, signode)
        signode.insert(0, addnodes.desc_addname('@', '@'))
        return ret

    def needs_arglist(self):
        return False

class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel):
    def run(self):
        # a decorator function is a function after all
        self.name = 'py:function'
        return PyModulelevel.run(self)

class PyDecoratorMethod(PyDecoratorMixin, PyClassmember):
    def run(self):
        self.name = 'py:method'
        return PyClassmember.run(self)

# Support for building "topic help" for pydoc

pydoc_topic_labels = [
    'assert', 'assignment', 'atom-identifiers', 'atom-literals',
    'attribute-access', 'attribute-references', 'augassign', 'binary',
    'bitwise', 'bltin-code-objects', 'bltin-ellipsis-object',
    'bltin-null-object', 'bltin-type-objects', 'booleans',
    'break', 'callable-types', 'calls', 'class', 'comparisons', 'compound',
    'context-managers', 'continue', 'conversions', 'customization', 'debugger',
    'del', 'dict', 'dynamic-features', 'else', 'exceptions', 'execmodel',
    'exprlists', 'floating', 'for', 'formatstrings', 'function', 'global',
    'id-classes', 'identifiers', 'if', 'imaginary', 'import', 'in', 'integers',
    'lambda', 'lists', 'naming', 'nonlocal', 'numbers', 'numeric-types',
    'objects', 'operator-summary', 'pass', 'power', 'raise', 'return',
    'sequence-types', 'shifting', 'slicings', 'specialattrs', 'specialnames',
    'string-methods', 'strings', 'subscriptions', 'truth', 'try', 'types',
    'typesfunctions', 'typesmapping', 'typesmethods', 'typesmodules',
    'typesseq', 'typesseq-mutable', 'unary', 'while', 'with', 'yield'
]

from os import path
from time import asctime
from pprint import pformat
from docutils.io import StringOutput
from docutils.utils import new_document

from sphinx.builders import Builder
from sphinx.writers.text import TextWriter

class PydocTopicsBuilder(Builder):
    name = 'pydoc-topics'

    def init(self):
        self.topics = {}

    def get_outdated_docs(self):
        return 'all pydoc topics'

    def get_target_uri(self, docname, typ=None):
        return ''  # no URIs

    def write(self, *ignored):
        writer = TextWriter(self)
        for label in self.status_iterator(pydoc_topic_labels,
                                          'building topics... ',
                                          length=len(pydoc_topic_labels)):
            if label not in self.env.domaindata['std']['labels']:
                self.warn('label %r not in documentation' % label)
                continue
            docname, labelid, sectname = self.env.domaindata['std']['labels'][label]
            doctree = self.env.get_and_resolve_doctree(docname, self)
            document = new_document('
')
            document.append(doctree.ids[labelid])
            destination = StringOutput(encoding='utf-8')
            writer.write(document, destination)
            self.topics[label] = str(writer.output)

    def finish(self):
        f = open(path.join(self.outdir, 'topics.py'), 'w')
        try:
            f.write('# Autogenerated by Sphinx on %s\n' % asctime())
            f.write('topics = ' + pformat(self.topics) + '\n')
        finally:
            f.close()

# Support for checking for suspicious markup

import suspicious

# Support for documenting Opcodes

import re

opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?')

def parse_opcode_signature(env, sig, signode):
    """Transform an opcode signature into RST nodes."""
    m = opcode_sig_re.match(sig)
    if m is None:
        raise ValueError
    opname, arglist = m.groups()
    signode += addnodes.desc_name(opname, opname)
    if arglist is not None:
        paramlist = addnodes.desc_parameterlist()
        signode += paramlist
        paramlist += addnodes.desc_parameter(arglist, arglist)
    return opname.strip()

pdbcmd_sig_re = re.compile(r'([a-z()!]+)\s*(.*)')

# later...
#pdbargs_tokens_re = re.compile(r'''[a-zA-Z]+  |  # identifiers
#                                   [.,:]+     |  # punctuation
#                                   [\[\]()]   |  # parens
#                                   \s+           # whitespace
#                                   ''', re.X)

def parse_pdb_command(env, sig, signode):
    """Transform a pdb command signature into RST nodes."""
    m = pdbcmd_sig_re.match(sig)
    if m is None:
        raise ValueError
    name, args = m.groups()
    fullname = name.replace('(', '').replace(')', '')
    signode += addnodes.desc_name(name, name)
    if args:
        signode += addnodes.desc_addname(' '+args, ' '+args)
    return fullname

def setup(app):
    app.add_role('issue', issue_role)
    app.add_role('source', source_role)
    app.add_directive('impl-detail', ImplementationDetail)
    app.add_builder(PydocTopicsBuilder)
    app.add_builder(suspicious.CheckSuspiciousMarkupBuilder)
    app.add_description_unit('opcode', 'opcode', '%s (opcode)',
                             parse_opcode_signature)
    app.add_description_unit('pdbcommand', 'pdbcmd', '%s (pdb command)',
                             parse_pdb_command)
    app.add_description_unit('2to3fixer', '2to3fixer', '%s (2to3 fixer)')
    app.add_directive_to_domain('py', 'decorator', PyDecoratorFunction)
    app.add_directive_to_domain('py', 'decoratormethod', PyDecoratorMethod)
<commit_msg>Add a deprecated-removed directive that allows to give the version of removal for deprecations.<commit_after># -*- coding: utf-8 -*-
"""
    pyspecific.py
    ~~~~~~~~~~~~~

    Sphinx extension with Python doc-specific markup.

    :copyright: 2008, 2009, 2010 by Georg Brandl.
    :license: Python license.
"""

ISSUE_URI = 'http://bugs.python.org/issue%s'
SOURCE_URI = 'http://svn.python.org/view/python/branches/py3k/%s?view=markup'

from docutils import nodes, utils
from sphinx.util.nodes import split_explicit_title

# monkey-patch reST parser to disable alphabetic and roman enumerated lists
from docutils.parsers.rst.states import Body
Body.enum.converters['loweralpha'] = \
    Body.enum.converters['upperalpha'] = \
    Body.enum.converters['lowerroman'] = \
    Body.enum.converters['upperroman'] = lambda x: None

# monkey-patch HTML translator to give versionmodified paragraphs a class
def new_visit_versionmodified(self, node):
    self.body.append(self.starttag(node, 'p', CLASS=node['type']))
    text = versionlabels[node['type']] % node['version']
    if len(node):
        text += ': '
    else:
        text += '.'
    self.body.append('%s' % text)

from sphinx.writers.html import HTMLTranslator
from sphinx.locale import versionlabels
HTMLTranslator.visit_versionmodified = new_visit_versionmodified

# Support for marking up and linking to bugs.python.org issues

def issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
    issue = utils.unescape(text)
    text = 'issue ' + issue
    refnode = nodes.reference(text, text, refuri=ISSUE_URI % issue)
    return [refnode], []

# Support for linking to Python source files easily

def source_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
    has_t, title, target = split_explicit_title(text)
    title = utils.unescape(title)
    target = utils.unescape(target)
    refnode = nodes.reference(title, title, refuri=SOURCE_URI % target)
    return [refnode], []

# Support for marking up implementation details

from sphinx.util.compat import Directive

class ImplementationDetail(Directive):

    has_content = True
    required_arguments = 0
    optional_arguments = 1
    final_argument_whitespace = True

    def run(self):
        pnode = nodes.compound(classes=['impl-detail'])
        content = self.content
        add_text = nodes.strong('CPython implementation detail:',
                                'CPython implementation detail:')
        if self.arguments:
            n, m = self.state.inline_text(self.arguments[0], self.lineno)
            pnode.append(nodes.paragraph('', '', *(n + m)))
        self.state.nested_parse(content, self.content_offset, pnode)
        if pnode.children and isinstance(pnode[0], nodes.paragraph):
            pnode[0].insert(0, add_text)
            pnode[0].insert(1, nodes.Text(' '))
        else:
            pnode.insert(0, nodes.paragraph('', '', add_text))
        return [pnode]

# Support for documenting decorators

from sphinx import addnodes
from sphinx.domains.python import PyModulelevel, PyClassmember

class PyDecoratorMixin(object):
    def handle_signature(self, sig, signode):
        ret = super(PyDecoratorMixin, self).handle_signature(sig, signode)
        signode.insert(0, addnodes.desc_addname('@', '@'))
        return ret

    def needs_arglist(self):
        return False

class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel):
    def run(self):
        # a decorator function is a function after all
        self.name = 'py:function'
        return PyModulelevel.run(self)

class PyDecoratorMethod(PyDecoratorMixin, PyClassmember):
    def run(self):
        self.name = 'py:method'
        return PyClassmember.run(self)

# Support for documenting version of removal in deprecations

from sphinx.locale import versionlabels
from sphinx.util.compat import Directive

versionlabels['deprecated-removed'] = \
    'Deprecated since version %s, will be removed in version %s'

class DeprecatedRemoved(Directive):
    has_content = True
    required_arguments = 2
    optional_arguments = 1
    final_argument_whitespace = True
    option_spec = {}

    def run(self):
        node = addnodes.versionmodified()
        node.document = self.state.document
        node['type'] = 'deprecated-removed'
        version = (self.arguments[0], self.arguments[1])
        node['version'] = version
        if len(self.arguments) == 3:
            inodes, messages = self.state.inline_text(self.arguments[2],
                                                      self.lineno+1)
            node.extend(inodes)
            if self.content:
                self.state.nested_parse(self.content, self.content_offset, node)
            ret = [node] + messages
        else:
            ret = [node]
        env = self.state.document.settings.env
        env.note_versionchange('deprecated', version[0], node, self.lineno)
        return ret

# Support for building "topic help" for pydoc

pydoc_topic_labels = [
    'assert', 'assignment', 'atom-identifiers', 'atom-literals',
    'attribute-access', 'attribute-references', 'augassign', 'binary',
    'bitwise', 'bltin-code-objects', 'bltin-ellipsis-object',
    'bltin-null-object', 'bltin-type-objects', 'booleans',
    'break', 'callable-types', 'calls', 'class', 'comparisons', 'compound',
    'context-managers', 'continue', 'conversions', 'customization', 'debugger',
    'del', 'dict', 'dynamic-features', 'else', 'exceptions', 'execmodel',
    'exprlists', 'floating', 'for', 'formatstrings', 'function', 'global',
    'id-classes', 'identifiers', 'if', 'imaginary', 'import', 'in', 'integers',
    'lambda', 'lists', 'naming', 'nonlocal', 'numbers', 'numeric-types',
    'objects', 'operator-summary', 'pass', 'power', 'raise', 'return',
    'sequence-types', 'shifting', 'slicings', 'specialattrs', 'specialnames',
    'string-methods', 'strings', 'subscriptions', 'truth', 'try', 'types',
    'typesfunctions', 'typesmapping', 'typesmethods', 'typesmodules',
    'typesseq', 'typesseq-mutable', 'unary', 'while', 'with', 'yield'
]

from os import path
from time import asctime
from pprint import pformat
from docutils.io import StringOutput
from docutils.utils import new_document

from sphinx.builders import Builder
from sphinx.writers.text import TextWriter

class PydocTopicsBuilder(Builder):
    name = 'pydoc-topics'

    def init(self):
        self.topics = {}

    def get_outdated_docs(self):
        return 'all pydoc topics'

    def get_target_uri(self, docname, typ=None):
        return ''  # no URIs

    def write(self, *ignored):
        writer = TextWriter(self)
        for label in self.status_iterator(pydoc_topic_labels,
                                          'building topics... ',
                                          length=len(pydoc_topic_labels)):
            if label not in self.env.domaindata['std']['labels']:
                self.warn('label %r not in documentation' % label)
                continue
            docname, labelid, sectname = self.env.domaindata['std']['labels'][label]
            doctree = self.env.get_and_resolve_doctree(docname, self)
            document = new_document('
')
            document.append(doctree.ids[labelid])
            destination = StringOutput(encoding='utf-8')
            writer.write(document, destination)
            self.topics[label] = str(writer.output)

    def finish(self):
        f = open(path.join(self.outdir, 'topics.py'), 'w')
        try:
            f.write('# Autogenerated by Sphinx on %s\n' % asctime())
            f.write('topics = ' + pformat(self.topics) + '\n')
        finally:
            f.close()

# Support for checking for suspicious markup

import suspicious

# Support for documenting Opcodes

import re

opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?')

def parse_opcode_signature(env, sig, signode):
    """Transform an opcode signature into RST nodes."""
    m = opcode_sig_re.match(sig)
    if m is None:
        raise ValueError
    opname, arglist = m.groups()
    signode += addnodes.desc_name(opname, opname)
    if arglist is not None:
        paramlist = addnodes.desc_parameterlist()
        signode += paramlist
        paramlist += addnodes.desc_parameter(arglist, arglist)
    return opname.strip()

# Support for documenting pdb commands

pdbcmd_sig_re = re.compile(r'([a-z()!]+)\s*(.*)')

# later...
#pdbargs_tokens_re = re.compile(r'''[a-zA-Z]+  |  # identifiers
#                                   [.,:]+     |  # punctuation
#                                   [\[\]()]   |  # parens
#                                   \s+           # whitespace
#                                   ''', re.X)

def parse_pdb_command(env, sig, signode):
    """Transform a pdb command signature into RST nodes."""
    m = pdbcmd_sig_re.match(sig)
    if m is None:
        raise ValueError
    name, args = m.groups()
    fullname = name.replace('(', '').replace(')', '')
    signode += addnodes.desc_name(name, name)
    if args:
        signode += addnodes.desc_addname(' '+args, ' '+args)
    return fullname

def setup(app):
    app.add_role('issue', issue_role)
    app.add_role('source', source_role)
    app.add_directive('impl-detail', ImplementationDetail)
    app.add_directive('deprecated-removed', DeprecatedRemoved)
    app.add_builder(PydocTopicsBuilder)
    app.add_builder(suspicious.CheckSuspiciousMarkupBuilder)
    app.add_description_unit('opcode', 'opcode', '%s (opcode)',
                             parse_opcode_signature)
    app.add_description_unit('pdbcommand', 'pdbcmd', '%s (pdb command)',
                             parse_pdb_command)
    app.add_description_unit('2to3fixer', '2to3fixer', '%s (2to3 fixer)')
    app.add_directive_to_domain('py', 'decorator', PyDecoratorFunction)
    app.add_directive_to_domain('py', 'decoratormethod', PyDecoratorMethod)
<|endoftext|>


<commit_before>flags = [
'-Wall',
'-Wextra',
'-Werror',
'-fexceptions',
'-DNDEBUG',
'-DUSE_CLANG_COMPLETER',
'-pthread',
'-I/usr/include/gtk-3.0',
'-I/usr/include/at-spi2-atk/2.0',
'-I/usr/include/gtk-3.0',
'-I/usr/include/gio-unix-2.0/',
'-I/usr/include/cairo',
'-I/usr/include/pango-1.0',
'-I/usr/include/harfbuzz',
'-I/usr/include/pango-1.0',
'-I/usr/include/atk-1.0',
'-I/usr/include/cairo',
'-I/usr/include/pixman-1',
'-I/usr/include/freetype2',
'-I/usr/include/libdrm',
'-I/usr/include/libpng16',
'-I/usr/include/gdk-pixbuf-2.0',
'-I/usr/include/libpng16',
'-I/usr/include/glib-2.0',
'-I/usr/lib/glib-2.0/include',
'-lgtk-3',
'-lgdk-3',
'-lpangocairo-1.0',
'-lpango-1.0',
'-latk-1.0',
'-lcairo-gobject',
'-lcairo',
'-lgdk_pixbuf-2.0',
'-lgio-2.0',
'-lgobject-2.0',
'-lglib-2.0'
]

def FlagsForFile( filename ):
  return {
    'flags': flags,
    'do_cache': True
  }
<commit_msg>[GTK3] Made YCM conf better<commit_after>import subprocess

flags = [
  '-Wall',
  '-Wextra',
  '-Werror',
  '-fexceptions',
  '-DNDEBUG',
  '-DUSE_CLANG_COMPLETER',
  '-pthread',
]

flags += [s.strip() for s in subprocess.check_output(['pkg-config', '--cflags', 'gtk+-3.0']).strip().split(' ')]
flags += [s.strip() for s in subprocess.check_output(['pkg-config', '--libs', 'gtk+-3.0']).strip().split(' ')]

def FlagsForFile( filename ):
  return {
    'flags': flags,
    'do_cache': True
  }
<|endoftext|>


<commit_before>from PyQt4 import QtGui

import numpy
from matplotlib.figure import Figure

from .canvasbase import CanvasBase

class LineScanCanvas(CanvasBase):
    """Class to represent the FigureCanvas widget"""

    def __init__(self, segmentation, selectionmodel):
        # initialize the canvas where the Figure renders into
        super(LineScanCanvas, self).__init__()
        self.segmentation = segmentation
        self.selectionmodel = selectionmodel
        self.branch = None
        self.imglinescan = None
        self.scatterevents = None
        self.axes = self.figure.add_subplot(111)
        self.mpl_connect('button_press_event', self.onclick)
        self.axes.set_xlim(0, self.segmentation.data.shape[-1])
        self.axes.autoscale(False, axis='x')
        self.figure.set_tight_layout(True)

        self.segmentation.active_frame_changed.append(self.on_active_frame_change)
        self.segmentation.overlay_changed.append(self.on_overlay_change)
        self.segmentation.data_changed.append(self.on_data_change)
        self.segmentation.postprocessor_changed.append(self.on_data_change)

        # cache calculated line scans in dictionary having the branch mask as key
        self.__linescans = {}

    def on_active_frame_change(self):
        if hasattr(self, "active_frame_line"):
            self.active_frame_line.remove()
        self.active_frame_line = self.axes.axvline(x=self.segmentation.active_frame + 0.5, color='black', lw=1.)
        self.draw()

    def on_overlay_change(self):
        self.__linescans.clear()
        # force update
        if self.branch is not None:
            self.redraw()

    def on_data_change(self):
        self.__linescans.clear()
        # force update
        if self.branch is not None:
            self.redraw()

    def set_branch(self, branch):
        if self.branch is branch:
            return
        # disconnect from the old branch
        if self.branch is not None:
            self.branch.changed.remove(self.on_branch_change)

        self.branch = branch
        self.branch.changed.append(self.on_branch_change)
        self.redraw()

    def redraw(self):
        with self.draw_on_exit():
            # remove the old linescan image
            if self.imglinescan is not None:
                self.imglinescan.remove()
                self.imglinescan = None
            if self.scatterevents is not None:
                self.scatterevents.remove()
                self.scatterevents = None

            if len(self.branch.children) > 0:
                tmax = self.segmentation.data.shape[-1]
                nsegments = len(self.branch.children)
                self.imglinescan = self.axes.imshow(self.linescan, interpolation='nearest', aspect='auto',
                                                    cmap='viridis', extent=(0, tmax, nsegments, 0))
                self.axes.set_ylim(nsegments, 0)

                from itertools import cycle
                cycol = cycle('bgrcm').next
                eventsx = []
                eventsy = []
                colors = []
                for i, child in enumerate(self.branch.children):
                    if hasattr(child, "events"):
                        indices = child.events.indices
                        if not hasattr(child, "color"):
                            child.color = cycol()
                        eventsx.append(child.events.indices)
                        eventsy.append(numpy.ones_like(child.events.indices) * (i + 0.5))
                        colors += [child.color for i in child.events.indices]
                if len(colors) > 0:
                    self.scatterevents = self.axes.scatter(numpy.concatenate(eventsx),
                                                           numpy.concatenate(eventsy),
                                                           color=colors)

    def on_branch_change(self, branch):
        """Will be called when the branch masks number of children change."""
        self.redraw()

    @property
    def linescan(self):
        """
        Calculate the trace for all children and return a 2D array aka linescan for that branch roi.
        """
        if self.branch in self.__linescans:
            return self.__linescans[self.branch]
        import numpy
        data = self.segmentation.data
        overlay = self.segmentation.overlay
        postprocessor = self.segmentation.postprocessor
        self.__linescans[self.branch] = numpy.row_stack((postprocessor(child(data, overlay)) for child in self.branch.children))
        return self.__linescans[self.branch]

    def onclick(self, event):
        if self.branch is not None and event.ydata is not None:
            index = int(event.ydata)
            if index < len(self.branch.children):
                # get the model underlying the selection
                model = self.selectionmodel.model()
                # get the model tree item for the selected segment
                treeitem = model.mask2roitreeitem[self.branch.children[index]]

                # clear selection and add the segment
                self.selectionmodel.clear()
                self.selectionmodel.select(model.createIndex(treeitem.parent().row(treeitem), 0, treeitem),
                                           QtGui.QItemSelectionModel.Select)
        if event.xdata is not None:
            self.segmentation.active_frame = event.xdata

class LineScanDockWidget(QtGui.QDockWidget):
    def __init__(self, name, parent, segmentation):
        super(LineScanDockWidget, self).__init__(name, parent)

        self.linescanwidget = LineScanCanvas(segmentation=segmentation, selectionmodel=parent.roiselectionmodel)

        from PyQt4 import QtCore
        from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT
        self.toolbar_navigation = NavigationToolbar2QT(self.linescanwidget, self, coordinates=False)
        self.toolbar_navigation.setOrientation(QtCore.Qt.Vertical)
        self.toolbar_navigation.setFloatable(True)

        self.widget = QtGui.QWidget()
        self.layout = QtGui.QHBoxLayout()
        self.layout.addWidget(self.toolbar_navigation)
        self.layout.addWidget(self.linescanwidget)

        self.widget.setLayout(self.layout)
        self.setWidget(self.widget)

    def set_branch(self, branch):
        self.linescanwidget.set_branch(branch)
<commit_msg>[FIX] make segment selection via linescan work again<commit_after>from PyQt4 import QtGui

import numpy
from matplotlib.figure import Figure

from .canvasbase import CanvasBase

class LineScanCanvas(CanvasBase):
    """Class to represent the FigureCanvas widget"""

    def __init__(self, segmentation, selectionmodel):
        # initialize the canvas where the Figure renders into
        super(LineScanCanvas, self).__init__()
        self.segmentation = segmentation
        self.selectionmodel = selectionmodel
        self.branch = None
        self.imglinescan = None
        self.scatterevents = None
        self.axes = self.figure.add_subplot(111)
        self.mpl_connect('button_press_event', self.onclick)
        self.axes.set_xlim(0, self.segmentation.data.shape[-1])
        self.axes.autoscale(False, axis='x')
        self.figure.set_tight_layout(True)

        self.segmentation.active_frame_changed.append(self.on_active_frame_change)
        self.segmentation.overlay_changed.append(self.on_overlay_change)
        self.segmentation.data_changed.append(self.on_data_change)
        self.segmentation.postprocessor_changed.append(self.on_data_change)

        # cache calculated line scans in dictionary having the branch mask as key
        self.__linescans = {}

    def on_active_frame_change(self):
        if hasattr(self, "active_frame_line"):
            self.active_frame_line.remove()
        self.active_frame_line = self.axes.axvline(x=self.segmentation.active_frame + 0.5, color='black', lw=1.)
        self.draw()

    def on_overlay_change(self):
        self.__linescans.clear()
        # force update
        if self.branch is not None:
            self.redraw()

    def on_data_change(self):
        self.__linescans.clear()
        # force update
        if self.branch is not None:
            self.redraw()

    def set_branch(self, branch):
        if self.branch is branch:
            return
        # disconnect from the old branch
        if self.branch is not None:
            self.branch.changed.remove(self.on_branch_change)

        self.branch = branch
        self.branch.changed.append(self.on_branch_change)
        self.redraw()

    def redraw(self):
        with self.draw_on_exit():
            # remove the old linescan image
            if self.imglinescan is not None:
                self.imglinescan.remove()
                self.imglinescan = None
            if self.scatterevents is not None:
                self.scatterevents.remove()
                self.scatterevents = None

            if len(self.branch.children) > 0:
                tmax = self.segmentation.data.shape[-1]
                nsegments = len(self.branch.children)
                self.imglinescan = self.axes.imshow(self.linescan, interpolation='nearest', aspect='auto',
                                                    cmap='viridis', extent=(0, tmax, nsegments, 0))
                self.axes.set_ylim(nsegments, 0)

                from itertools import cycle
                cycol = cycle('bgrcm').next
                eventsx = []
                eventsy = []
                colors = []
                for i, child in enumerate(self.branch.children):
                    if hasattr(child, "events"):
                        indices = child.events.indices
                        if not hasattr(child, "color"):
                            child.color = cycol()
                        eventsx.append(child.events.indices)
                        eventsy.append(numpy.ones_like(child.events.indices) * (i + 0.5))
                        colors += [child.color for i in child.events.indices]
                if len(colors) > 0:
                    self.scatterevents = self.axes.scatter(numpy.concatenate(eventsx),
                                                           numpy.concatenate(eventsy),
                                                           color=colors)

    def on_branch_change(self, branch):
        """Will be called when the branch masks number of children change."""
        self.redraw()

    @property
    def linescan(self):
        """
        Calculate the trace for all children and return a 2D array aka linescan for that branch roi.
        """
        if self.branch in self.__linescans:
            return self.__linescans[self.branch]
        import numpy
        data = self.segmentation.data
        overlay = self.segmentation.overlay
        postprocessor = self.segmentation.postprocessor
        self.__linescans[self.branch] = numpy.row_stack(
            (postprocessor(child(data, overlay)) for child in self.branch.children))
        return self.__linescans[self.branch]

    def onclick(self, event):
        if self.branch is not None and event.ydata is not None:
            index = int(event.ydata)
            if index < len(self.branch.children):
                # get the model underlying the selection
                model = self.selectionmodel.model()

                # clear selection and add the segment
                self.selectionmodel.clear()
                self.selectionmodel.select(model.find(self.branch.children[index]), QtGui.QItemSelectionModel.Select)
        if event.xdata is not None:
            self.segmentation.active_frame = event.xdata

class LineScanDockWidget(QtGui.QDockWidget):
    def __init__(self, name, parent, segmentation):
        super(LineScanDockWidget, self).__init__(name, parent)

        self.linescanwidget = LineScanCanvas(segmentation=segmentation, selectionmodel=parent.roiselectionmodel)

        from PyQt4 import QtCore
        from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT
        self.toolbar_navigation = NavigationToolbar2QT(self.linescanwidget, self, coordinates=False)
        self.toolbar_navigation.setOrientation(QtCore.Qt.Vertical)
        self.toolbar_navigation.setFloatable(True)

        self.widget = QtGui.QWidget()
        self.layout = QtGui.QHBoxLayout()
        self.layout.addWidget(self.toolbar_navigation)
        self.layout.addWidget(self.linescanwidget)

        self.widget.setLayout(self.layout)
        self.setWidget(self.widget)

    def set_branch(self, branch):
        self.linescanwidget.set_branch(branch)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
import re
from IRCMessage import IRCMessage
from IRCResponse import IRCResponse, ResponseType
from CommandInterface import CommandInterface
from Utils import WebUtils

import json
from twisted.internet import task, threads

class DominosPizza(CommandInterface):
    help = "Automatic module that tracks Domino's pizza tracker links, " \
           "informing you of the progress of your pizza until delivery"

    runInThread = True

    def onLoad(self):
        self.trackers = {}
        """@type : dict[str, TrackingDetails]"""

        self.regex = r'www\.dominos\.(co\.uk|\.ie)/pizzatracker/?\?id=(?P<orderID>[a-zA-Z0-9=]+)'

        commands = self.bot.moduleHandler.commands
        if 'URLFollow' in commands:
            commands['URLFollow'].handledExternally['DominosPizza'] = [self.regex]

    def onUnload(self):
        self._stopAllPizzaTrackers()
        commands = self.bot.moduleHandler.commands
        if 'URLFollow' in commands and 'DominosPizza' in commands['URLFollow'].handledExternally:
            del commands['URLFollow'].handledExternally['DominosPizza']

    def shouldExecute(self, message):
        """
        @type message: IRCMessage
        """
        if message.Type in self.acceptedTypes:
            return True

    def execute(self, message):
        """
        @type message: IRCMessage
        """
        match = re.search(self.regex, message.MessageString, re.IGNORECASE)

        if not match:
            return

        orderID = match.group('orderID')

        if orderID not in self.trackers:
            self.trackers[orderID] = TrackingDetails(message.User.Name, message.Channel,
                                                     task.LoopingCall(self._pizzaLoop, orderID))
            self._startPizzaTracker(orderID)
            return IRCResponse(ResponseType.Say,
                               u"PIZZA DETECTED! Now tracking {}'s Domino's pizza order!".format(message.User.Name),
                               message.ReplyTo)
        else:
            return IRCResponse(ResponseType.Say,
                               u"I'm already tracking that pizza for {}".format(self.trackers[orderID].orderer),
                               message.ReplyTo)

    def _startPizzaTracker(self, orderID):
        """
        @type orderID: str
        """
        self.trackers[orderID].tracker.start(30)

    def _pizzaLoop(self, orderID):
        return threads.deferToThread(self._pizzaTracker, orderID)

    def _pizzaTracker(self, orderID):
        """
        @type orderID: str
        """
        steps = {6: u"{}'s pizza order has been placed",
                 7: u"{}'s pizza is being prepared",
                 5: u"{}'s pizza is in the oven",
                 8: u"{}'s pizza is sitting on a shelf, waiting for a driver",
                 9: u"{}'s pizza is out for delivery",
                 3: u"{}'s pizza has been delivered! Tracking stopped"}

        trackingDetails = self.trackers[orderID]

        trackURL = u'https://www.dominos.co.uk/pizzaTracker/getOrderDetails?id={}'.format(orderID)
        page = WebUtils.fetchURL(trackURL)

        if page is None:
            # tracking API didn't respond
            self._stopPizzaTracker(orderID)
            self.bot.sendResponse(IRCResponse(ResponseType.Say,
                                  u"The pizza tracking page linked by {} "
                                  u"had some kind of error, tracking stopped".format(trackingDetails.orderer),
                                  trackingDetails.channel.Name))
            return

        j = json.loads(page.body)

        if j['customerName'] is None:
            self._stopPizzaTracker(orderID)
            self.bot.sendResponse(IRCResponse(ResponseType.Say,
                                  u"There are no pizza tracking details at the page linked by {}.".format(trackingDetails.orderer),
                                  trackingDetails.channel.Name))
            return

        step = j['statusId']
        if step != trackingDetails.step:
            trackingDetails.step = step
            response = IRCResponse(ResponseType.Say,
                                   steps[step-1].format(trackingDetails.orderer),
                                   trackingDetails.channel.Name)

        if step == 3:
            self._stopPizzaTracker(orderID)

        self.bot.sendResponse(response)

    def _stopPizzaTracker(self, orderID):
        """
        @type orderID: str
        """
        if orderID in self.trackers:
            if self.trackers[orderID].tracker.running:
                self.trackers[orderID].tracker.stop()
            del self.trackers[orderID]
            return True
        return False

    def _stopAllPizzaTrackers(self):
        for orderID in self.trackers.keys():
            self._stopPizzaTracker(orderID)

class TrackingDetails(object):
    def __init__(self, orderer, channel, tracker):
        """
        @type orderer: str
        @type channel: IRCChannel
        @type tracker: task.LoopingCall
        """
        self.orderer = orderer
        self.channel = channel
        self.tracker = tracker
        self.step = 0
<commit_msg>[DominosPizza] ...woops<commit_after># -*- coding: utf-8 -*-
import re
from IRCMessage import IRCMessage
from IRCResponse import IRCResponse, ResponseType
from CommandInterface import CommandInterface
from Utils import WebUtils

import json
from twisted.internet import task, threads

class DominosPizza(CommandInterface):
    help = "Automatic module that tracks Domino's pizza tracker links, " \
           "informing you of the progress of your pizza until delivery"

    runInThread = True

    def onLoad(self):
        self.trackers = {}
        """@type : dict[str, TrackingDetails]"""

        self.regex = r'www\.dominos\.(co\.uk|ie)/pizzatracker/?\?id=(?P<orderID>[a-zA-Z0-9=]+)'

        commands = self.bot.moduleHandler.commands
        if 'URLFollow' in commands:
            commands['URLFollow'].handledExternally['DominosPizza'] = [self.regex]

    def onUnload(self):
        self._stopAllPizzaTrackers()
        commands = self.bot.moduleHandler.commands
        if 'URLFollow' in commands and 'DominosPizza' in commands['URLFollow'].handledExternally:
            del commands['URLFollow'].handledExternally['DominosPizza']

    def shouldExecute(self, message):
        """
        @type message: IRCMessage
        """
        if message.Type in self.acceptedTypes:
            return True

    def execute(self, message):
        """
        @type message: IRCMessage
        """
        match = re.search(self.regex, message.MessageString, re.IGNORECASE)

        if not match:
            return

        orderID = match.group('orderID')

        if orderID not in self.trackers:
            self.trackers[orderID] = TrackingDetails(message.User.Name, message.Channel,
                                                     task.LoopingCall(self._pizzaLoop, orderID))
            self._startPizzaTracker(orderID)
            return IRCResponse(ResponseType.Say,
                               u"PIZZA DETECTED! Now tracking {}'s Domino's pizza order!".format(message.User.Name),
                               message.ReplyTo)
        else:
            return IRCResponse(ResponseType.Say,
                               u"I'm already tracking that pizza for {}".format(self.trackers[orderID].orderer),
                               message.ReplyTo)

    def _startPizzaTracker(self, orderID):
        """
        @type orderID: str
        """
        self.trackers[orderID].tracker.start(30)

    def _pizzaLoop(self, orderID):
        return threads.deferToThread(self._pizzaTracker, orderID)

    def _pizzaTracker(self, orderID):
        """
        @type orderID: str
        """
        steps = {6: u"{}'s pizza order has been placed",
                 7: u"{}'s pizza is being prepared",
                 5: u"{}'s pizza is in the oven",
                 8: u"{}'s pizza is sitting on a shelf, waiting for a driver",
                 9: u"{}'s pizza is out for delivery",
                 3: u"{}'s pizza has been delivered! Tracking stopped"}

        trackingDetails = self.trackers[orderID]

        trackURL = u'https://www.dominos.co.uk/pizzaTracker/getOrderDetails?id={}'.format(orderID)
        page = WebUtils.fetchURL(trackURL)

        if page is None:
            # tracking API didn't respond
            self._stopPizzaTracker(orderID)
            self.bot.sendResponse(IRCResponse(ResponseType.Say,
                                  u"The pizza tracking page linked by {} "
                                  u"had some kind of error, tracking stopped".format(trackingDetails.orderer),
                                  trackingDetails.channel.Name))
            return

        j = json.loads(page.body)

        if j['customerName'] is None:
            self._stopPizzaTracker(orderID)
            self.bot.sendResponse(IRCResponse(ResponseType.Say,
                                  u"There are no pizza tracking details at the page linked by {}.".format(trackingDetails.orderer),
                                  trackingDetails.channel.Name))
            return

        step = j['statusId']
        if step != trackingDetails.step:
            trackingDetails.step = step
            response = IRCResponse(ResponseType.Say,
                                   steps[step-1].format(trackingDetails.orderer),
                                   trackingDetails.channel.Name)

        if step == 3:
            self._stopPizzaTracker(orderID)

        self.bot.sendResponse(response)

    def _stopPizzaTracker(self, orderID):
        """
        @type orderID: str
        """
        if orderID in self.trackers:
            if self.trackers[orderID].tracker.running:
                self.trackers[orderID].tracker.stop()
            del self.trackers[orderID]
            return True
        return False

    def _stopAllPizzaTrackers(self):
        for orderID in self.trackers.keys():
            self._stopPizzaTracker(orderID)

class TrackingDetails(object):
    def __init__(self, orderer, channel, tracker):
        """
        @type orderer: str
        @type channel: IRCChannel
        @type tracker: task.LoopingCall
        """
        self.orderer = orderer
        self.channel = channel
        self.tracker = tracker
        self.step = 0
<|endoftext|>


<commit_before>from redbot.core import commands, checks, data_manager, Config
import tempfile
import gtts
import discord
import asyncio
import os
import random
import lavalink
import pydub
import aiohttp

class SFX(commands.Cog):
    """Plays uploaded sounds or text-to-speech using gTTS."""

    def __init__(self):
        self.tts_languages = list(gtts.lang.tts_langs().keys())
        self.last_track_info = None
        self.current_sfx = None
        self.config = Config.get_conf(self, identifier=134621854878007296)
        self.sound_base = (data_manager.cog_data_path(self) / 'sounds').as_posix()
        self.session = aiohttp.ClientSession()
        default_config = {
            'padding': 700,
            'tts_lang': 'en',
            'sounds': {}
        }
        self.config.register_guild(**default_config)
        lavalink.register_event_listener(self.ll_check)
        if not os.path.exists(self.sound_base):
            os.makedirs(self.sound_base)

    def __unload(self):
        lavalink.unregister_event_listener(self.ll_check)

    @commands.command(usage='[language code] <text>')
    @commands.cooldown(rate=1, per=1, type=discord.ext.commands.cooldowns.BucketType.guild)
    async def tts(self, ctx, *, text):
        """
        Plays the given text as TTS in your current voice channel.

        Turns a string of text into audio using the server's default language, if none is specified.
        Use `[p]ttslangs` for a list of valid language codes.
        """

        if not ctx.author.voice or not ctx.author.voice.channel:
            await ctx.send('You are not connected to a voice channel.')
            return

        cfg_tts_lang = await self.config.guild(ctx.guild).tts_lang()
        cfg_padding = await self.config.guild(ctx.guild).padding()
        lang = cfg_tts_lang
        try:
            lang, text = text.split(' ', maxsplit=1)
            if lang not in self.tts_languages:
                text = f'{lang} {text}'
                lang = cfg_tts_lang
        except ValueError:
            pass

        tts_audio = gtts.gTTS(text, lang=lang)
        audio_file = os.path.join(tempfile.gettempdir(), ''.join(random.choice('0123456789ABCDEF') for i in range(12)) + '.mp3')
        tts_audio.save(audio_file)
        audio_data = pydub.AudioSegment.from_mp3(audio_file)
        silence = pydub.AudioSegment.silent(duration=cfg_padding)
        padded_audio = silence + audio_data + silence
        padded_audio.export(audio_file, format='mp3')
        await self._play_sfx(ctx.author.voice.channel, audio_file, True)

    @commands.group()
    async def sfxconfig(self, ctx):
        """Configures the SFX cog."""
        pass

    @sfxconfig.command(usage='[language code]')
    @checks.mod()
    async def tts_lang(self, ctx, lang: str = None):
        """
        Configures the default TTS language.

        Gets/sets the default language for the `[p]tts` command.
        Use `[p]ttslangs` for a list of language codes.
        """

        cfg_tts_lang = await self.config.guild(ctx.guild).tts_lang()
        if lang is None:
            await ctx.send(f"Current value of `tts_lang`: {cfg_tts_lang}")
            return

        if lang not in self.tts_languages:
            await ctx.send('Invalid langauge. Use [p]ttsconfig langlist for a list of languages.')
            return

        cfg_tts_lang = lang
        await self.config.guild(ctx.guild).tts_lang.set(cfg_tts_lang)
        await ctx.send(f'`tts_lang` set to {lang}.')

    @sfxconfig.command(usage='<duration>')
    @checks.mod()
    async def padding(self, ctx, padding: int = None):
        """
        Configures the default padding.

        Gets/sets the default duration of padding (in ms) for the `[p]tts` and `[p]addsfx` commands.
        Adjust if the sound gets cut off at the beginning or the end.

        Warning: Sounds do not get affected immediately. You have to re-add them for this to have an effect on them.
        TTS gets affected immediately.
        """

        cfg_padding = await self.config.guild(ctx.guild).padding()
        if padding is None:
            await ctx.send(f"Current value of `padding`: {cfg_padding}")
            return

        cfg_padding = padding
        await self.config.guild(ctx.guild).padding.set(cfg_padding)
        await ctx.send(f'`padding` set to {padding}.')

    @commands.command()
    @commands.cooldown(rate=1, per=2, type=discord.ext.commands.cooldowns.BucketType.guild)
    async def ttslangs(self, ctx):
        """
        Lists available TTS Languages.

        Prints the list of valid languages for use with `[p]tts`.
        """

        await ctx.send(f"List of valid languages: {', '.join(self.tts_languages)}")

    @commands.command()
    @commands.cooldown(rate=1, per=1, type=discord.ext.commands.cooldowns.BucketType.guild)
    async def sfx(self, ctx, soundname: str):
        """
        Plays an existing sound in your current voice channel.
        """

        if not ctx.author.voice or not ctx.author.voice.channel:
            await ctx.send('You are not connected to a voice channel.')
            return

        if str(ctx.guild.id) not in os.listdir(self.sound_base):
            os.makedirs(os.path.join(self.sound_base, str(ctx.guild.id)))

        cfg_sounds = await self.config.guild(ctx.guild).sounds()
        if soundname not in cfg_sounds.keys():
            await ctx.send(f'Sound `{soundname}` does not exist. Try `{ctx.prefix}allsfx` for a list.')
            return

        filepath = os.path.join(self.sound_base, str(ctx.guild.id), cfg_sounds[soundname])
        if not os.path.exists(filepath):
            del cfg_sounds[soundname]
            await self.config.guild(ctx.guild).sounds.set(cfg_sounds)
            await ctx.send('Looks like this sound\'s file has gone missing! I\'ve removed it from the list of sounds.')
            return

        await self._play_sfx(ctx.author.voice.channel, filepath)

    @commands.command()
    @checks.mod()
    async def addsfx(self, ctx, name: str, link: str=None):
        """Adds a new sound.

        Either upload the file as a Discord attachment and make your comment
        `[p]addsfx <name>`, or use `[p]addsfx <name> <direct-URL-to-file>`.
        """
        cfg_sounds = await self.config.guild(ctx.guild).sounds()
        cfg_padding = await self.config.guild(ctx.guild).padding()

        if str(ctx.guild.id) not in os.listdir(self.sound_base):
            os.makedirs(os.path.join(self.sound_base, str(ctx.guild.id)))

        attach = ctx.message.attachments
        if len(attach) > 1 or (attach and link):
            await ctx.send('Please only add one sound at a time.')
            return

        url = ''
        filename = ''
        if attach:
            a = attach[0]
            url = a.url
            filename = a.filename
        elif link:
            url = ''.join(link)
            filename = os.path.basename(
                '_'.join(url.split()).replace('%20', '_'))
        else:
            await ctx.send('You must provide either a Discord attachment or a direct link to a sound.')
            return

        _, file_extension = os.path.splitext(filename)
        if file_extension != '.wav' and file_extension != '.mp3':
            await ctx.send('Only .wav and .mp3 sounds are currently supported.')
            return

        filepath = os.path.join(self.sound_base, str(ctx.guild.id), filename)

        if name in cfg_sounds.keys():
            await ctx.send('A sound with that name already exists. Please choose another name and try again.')
            return

        if os.path.exists(filepath):
            await ctx.send('A sound with that filename already exists. Please change the filename and try again.')
            return

        async with self.session.get(url) as new_sound:
            f = open(filepath, 'wb')
            f.write(await new_sound.read())
            f.close()

        audio_data = pydub.AudioSegment.from_file(filepath, format=file_extension[1:])
        silence = pydub.AudioSegment.silent(duration=cfg_padding)
        padded_audio = silence + audio_data + silence
        padded_audio.export(filepath, format=file_extension[1:])

        cfg_sounds[name] = filename
        await self.config.guild(ctx.guild).sounds.set(cfg_sounds)

        await ctx.send(f'Sound {name} added.')

    @commands.command()
    @checks.mod()
    async def delsfx(self, ctx, soundname: str):
        """Deletes an existing sound."""

        if str(ctx.guild.id) not in os.listdir(self.sound_base):
            os.makedirs(os.path.join(self.sound_base, str(ctx.guild.id)))

        cfg_sounds = await self.config.guild(ctx.guild).sounds()

        if soundname not in cfg_sounds.keys():
            await ctx.send(f'Sound `{soundname}` does not exist. Try `{ctx.prefix}allsfx` for a list.')
            return

        filepath = os.path.join(self.sound_base, str(ctx.guild.id), cfg_sounds[soundname])

        if os.path.exists(filepath):
            os.remove(filepath)

        del cfg_sounds[soundname]
        await self.config.guild(ctx.guild).sounds.set(cfg_sounds)

        await ctx.send(f'Sound {soundname} deleted.')

    @commands.command()
    @commands.cooldown(rate=1, per=3, type=discord.ext.commands.cooldowns.BucketType.guild)
    async def allsfx(self, ctx):
        """Prints all available sounds for this server."""

        if str(ctx.guild.id) not in os.listdir(self.sound_base):
            os.makedirs(os.path.join(self.sound_base, str(ctx.guild.id)))

        cfg_sounds = await self.config.guild(ctx.guild).sounds()

        if len(cfg_sounds.items()) == 0:
            await ctx.send(f'No sounds found. Use `{ctx.prefix}addsfx` to add one.')
            return

        paginator = discord.ext.commands.formatter.Paginator()
        for soundname, filepath in cfg_sounds.items():
            paginator.add_line(soundname)

        await ctx.send('Sounds for this server:')
        for page in paginator.pages:
            await ctx.send(page)

    @commands.command(no_pm=True, pass_context=True, aliases=['getsound'])
    @commands.cooldown(rate=1, per=2, type=discord.ext.commands.cooldowns.BucketType.guild)
    async def getsfx(self, ctx, soundname: str):
        """Uploads the given sound."""

        if str(ctx.guild.id) not in os.listdir(self.sound_base):
            os.makedirs(os.path.join(self.sound_base, str(ctx.guild.id)))

        cfg_sounds = await self.config.guild(ctx.guild).sounds()

        if soundname not in cfg_sounds.keys():
            await ctx.send(f'Sound `{soundname}` does not exist. Try `{ctx.prefix}allsfx` for a list.')
            return

        filepath = os.path.join(self.sound_base, str(ctx.guild.id), cfg_sounds[soundname])
        if not os.path.exists(filepath):
            del cfg_sounds[soundname]
            await self.config.guild(ctx.guild).sounds.set(cfg_sounds)
            await ctx.send('Looks like this sound\'s file has gone missing! I\'ve removed it from the list of sounds.')
            return

        await ctx.send(file=discord.File(filepath))

    async def _play_sfx(self, vc, filepath, is_tts=False):
        player = await lavalink.connect(vc)
        track = (await player.get_tracks(query=filepath))[0]

        if player.current is None:
            player.queue.append(track)
            self.current_sfx = (track, is_tts)
            await player.play()
            return

        if self.current_sfx is not None:
            player.queue.insert(0, track)
            await player.skip()
            if self.current_sfx[1]:
                os.remove(self.current_sfx[0].uri)
            self.current_sfx = (track, is_tts)
            return

        self.last_track_info = (player.current, player.position)
        self.current_sfx = (track, is_tts)
        player.queue.insert(0, track)
        player.queue.insert(1, player.current)
        await player.skip()

    async def ll_check(self, player, event, reason):
        if self.current_sfx is None and self.last_track_info is None:
            return

        if event == lavalink.LavalinkEvents.TRACK_EXCEPTION and self.current_sfx is not None:
            if self.current_sfx[1]:
                os.remove(self.current_sfx[0].uri)
            self.current_sfx = None
            return

        if event == lavalink.LavalinkEvents.TRACK_STUCK and self.current_sfx is not None:
            if self.current_sfx[1]:
                os.remove(self.current_sfx[0].uri)
            self.current_sfx = None
            await player.skip()
            return

        if event == lavalink.LavalinkEvents.TRACK_END and player.current is None and self.current_sfx is not None:
            if self.current_sfx[1]:
                os.remove(self.current_sfx[0].uri)
            self.current_sfx = None
            return

        if event == lavalink.LavalinkEvents.TRACK_END and player.current.track_identifier == self.last_track_info[0].track_identifier:
            print(str(self.last_track_info[0].uri))
            if self.current_sfx[1]:
                os.remove(self.current_sfx[0].uri)
            self.current_sfx = None
            await player.pause()
            await player.seek(self.last_track_info[1])
            await player.pause(False)
            self.last_track_info = None<commit_msg>[SFX] Fixed [p]allsfx after discord.py update<commit_after>from redbot.core import commands, checks, data_manager, Config
import tempfile
import gtts
import discord
import asyncio
import os
import random
import lavalink
import pydub
import aiohttp

class SFX(commands.Cog):
    """Plays uploaded sounds or text-to-speech using gTTS."""

    def __init__(self):
        self.tts_languages = list(gtts.lang.tts_langs().keys())
        self.last_track_info = None
        self.current_sfx = None
        self.config = Config.get_conf(self, identifier=134621854878007296)
        self.sound_base = (data_manager.cog_data_path(self) / 'sounds').as_posix()
        self.session = aiohttp.ClientSession()
        default_config = {
            'padding': 700,
            'tts_lang': 'en',
            'sounds': {}
        }
        self.config.register_guild(**default_config)
        lavalink.register_event_listener(self.ll_check)
        if not os.path.exists(self.sound_base):
            os.makedirs(self.sound_base)

    def __unload(self):
        lavalink.unregister_event_listener(self.ll_check)

    @commands.command(usage='[language code] <text>')
    @commands.cooldown(rate=1, per=1, type=discord.ext.commands.cooldowns.BucketType.guild)
    async def tts(self, ctx, *, text):
        """
        Plays the given text as TTS in your current voice channel.

        Turns a string of text into audio using the server's default language, if none is specified.
        Use `[p]ttslangs` for a list of valid language codes.
        """

        if not ctx.author.voice or not ctx.author.voice.channel:
            await ctx.send('You are not connected to a voice channel.')
            return

        cfg_tts_lang = await self.config.guild(ctx.guild).tts_lang()
        cfg_padding = await self.config.guild(ctx.guild).padding()
        lang = cfg_tts_lang
        try:
            lang, text = text.split(' ', maxsplit=1)
            if lang not in self.tts_languages:
                text = f'{lang} {text}'
                lang = cfg_tts_lang
        except ValueError:
            pass

        tts_audio = gtts.gTTS(text, lang=lang)
        audio_file = os.path.join(tempfile.gettempdir(), ''.join(random.choice('0123456789ABCDEF') for i in range(12)) + '.mp3')
        tts_audio.save(audio_file)
        audio_data = pydub.AudioSegment.from_mp3(audio_file)
        silence = pydub.AudioSegment.silent(duration=cfg_padding)
        padded_audio = silence + audio_data + silence
        padded_audio.export(audio_file, format='mp3')
        await self._play_sfx(ctx.author.voice.channel, audio_file, True)

    @commands.group()
    async def sfxconfig(self, ctx):
        """Configures the SFX cog."""
        pass

    @sfxconfig.command(usage='[language code]')
    @checks.mod()
    async def tts_lang(self, ctx, lang: str = None):
        """
        Configures the default TTS language.

        Gets/sets the default language for the `[p]tts` command.
        Use `[p]ttslangs` for a list of language codes.
        """

        cfg_tts_lang = await self.config.guild(ctx.guild).tts_lang()
        if lang is None:
            await ctx.send(f"Current value of `tts_lang`: {cfg_tts_lang}")
            return

        if lang not in self.tts_languages:
            await ctx.send('Invalid langauge. Use [p]ttsconfig langlist for a list of languages.')
            return

        cfg_tts_lang = lang
        await self.config.guild(ctx.guild).tts_lang.set(cfg_tts_lang)
        await ctx.send(f'`tts_lang` set to {lang}.')

    @sfxconfig.command(usage='<duration>')
    @checks.mod()
    async def padding(self, ctx, padding: int = None):
        """
        Configures the default padding.

        Gets/sets the default duration of padding (in ms) for the `[p]tts` and `[p]addsfx` commands.
        Adjust if the sound gets cut off at the beginning or the end.

        Warning: Sounds do not get affected immediately. You have to re-add them for this to have an effect on them.
        TTS gets affected immediately.
        """

        cfg_padding = await self.config.guild(ctx.guild).padding()
        if padding is None:
            await ctx.send(f"Current value of `padding`: {cfg_padding}")
            return

        cfg_padding = padding
        await self.config.guild(ctx.guild).padding.set(cfg_padding)
        await ctx.send(f'`padding` set to {padding}.')

    @commands.command()
    @commands.cooldown(rate=1, per=2, type=discord.ext.commands.cooldowns.BucketType.guild)
    async def ttslangs(self, ctx):
        """
        Lists available TTS Languages.

        Prints the list of valid languages for use with `[p]tts`.
        """

        await ctx.send(f"List of valid languages: {', '.join(self.tts_languages)}")

    @commands.command()
    @commands.cooldown(rate=1, per=1, type=discord.ext.commands.cooldowns.BucketType.guild)
    async def sfx(self, ctx, soundname: str):
        """
        Plays an existing sound in your current voice channel.
        """

        if not ctx.author.voice or not ctx.author.voice.channel:
            await ctx.send('You are not connected to a voice channel.')
            return

        if str(ctx.guild.id) not in os.listdir(self.sound_base):
            os.makedirs(os.path.join(self.sound_base, str(ctx.guild.id)))

        cfg_sounds = await self.config.guild(ctx.guild).sounds()
        if soundname not in cfg_sounds.keys():
            await ctx.send(f'Sound `{soundname}` does not exist. Try `{ctx.prefix}allsfx` for a list.')
            return

        filepath = os.path.join(self.sound_base, str(ctx.guild.id), cfg_sounds[soundname])
        if not os.path.exists(filepath):
            del cfg_sounds[soundname]
            await self.config.guild(ctx.guild).sounds.set(cfg_sounds)
            await ctx.send('Looks like this sound\'s file has gone missing! I\'ve removed it from the list of sounds.')
            return

        await self._play_sfx(ctx.author.voice.channel, filepath)

    @commands.command()
    @checks.mod()
    async def addsfx(self, ctx, name: str, link: str=None):
        """Adds a new sound.

        Either upload the file as a Discord attachment and make your comment
        `[p]addsfx <name>`, or use `[p]addsfx <name> <direct-URL-to-file>`.
        """
        cfg_sounds = await self.config.guild(ctx.guild).sounds()
        cfg_padding = await self.config.guild(ctx.guild).padding()

        if str(ctx.guild.id) not in os.listdir(self.sound_base):
            os.makedirs(os.path.join(self.sound_base, str(ctx.guild.id)))

        attach = ctx.message.attachments
        if len(attach) > 1 or (attach and link):
            await ctx.send('Please only add one sound at a time.')
            return

        url = ''
        filename = ''
        if attach:
            a = attach[0]
            url = a.url
            filename = a.filename
        elif link:
            url = ''.join(link)
            filename = os.path.basename(
                '_'.join(url.split()).replace('%20', '_'))
        else:
            await ctx.send('You must provide either a Discord attachment or a direct link to a sound.')
            return

        _, file_extension = os.path.splitext(filename)
        if file_extension != '.wav' and file_extension != '.mp3':
            await ctx.send('Only .wav and .mp3 sounds are currently supported.')
            return

        filepath = os.path.join(self.sound_base, str(ctx.guild.id), filename)

        if name in cfg_sounds.keys():
            await ctx.send('A sound with that name already exists. Please choose another name and try again.')
            return

        if os.path.exists(filepath):
            await ctx.send('A sound with that filename already exists. Please change the filename and try again.')
            return

        async with self.session.get(url) as new_sound:
            f = open(filepath, 'wb')
            f.write(await new_sound.read())
            f.close()

        audio_data = pydub.AudioSegment.from_file(filepath, format=file_extension[1:])
        silence = pydub.AudioSegment.silent(duration=cfg_padding)
        padded_audio = silence + audio_data + silence
        padded_audio.export(filepath, format=file_extension[1:])

        cfg_sounds[name] = filename
        await self.config.guild(ctx.guild).sounds.set(cfg_sounds)

        await ctx.send(f'Sound {name} added.')

    @commands.command()
    @checks.mod()
    async def delsfx(self, ctx, soundname: str):
        """Deletes an existing sound."""

        if str(ctx.guild.id) not in os.listdir(self.sound_base):
            os.makedirs(os.path.join(self.sound_base, str(ctx.guild.id)))

        cfg_sounds = await self.config.guild(ctx.guild).sounds()

        if soundname not in cfg_sounds.keys():
            await ctx.send(f'Sound `{soundname}` does not exist. Try `{ctx.prefix}allsfx` for a list.')
            return

        filepath = os.path.join(self.sound_base, str(ctx.guild.id), cfg_sounds[soundname])

        if os.path.exists(filepath):
            os.remove(filepath)

        del cfg_sounds[soundname]
        await self.config.guild(ctx.guild).sounds.set(cfg_sounds)

        await ctx.send(f'Sound {soundname} deleted.')

    @commands.command()
    @commands.cooldown(rate=1, per=3, type=discord.ext.commands.cooldowns.BucketType.guild)
    async def allsfx(self, ctx):
        """Prints all available sounds for this server."""

        if str(ctx.guild.id) not in os.listdir(self.sound_base):
            os.makedirs(os.path.join(self.sound_base, str(ctx.guild.id)))

        cfg_sounds = await self.config.guild(ctx.guild).sounds()

        if len(cfg_sounds.items()) == 0:
            await ctx.send(f'No sounds found. Use `{ctx.prefix}addsfx` to add one.')
            return

        paginator = discord.ext.commands.help.Paginator()
        for soundname, filepath in cfg_sounds.items():
            paginator.add_line(soundname)

        await ctx.send('Sounds for this server:')
        for page in paginator.pages:
            await ctx.send(page)

    @commands.command(no_pm=True, pass_context=True, aliases=['getsound'])
    @commands.cooldown(rate=1, per=2, type=discord.ext.commands.cooldowns.BucketType.guild)
    async def getsfx(self, ctx, soundname: str):
        """Uploads the given sound."""

        if str(ctx.guild.id) not in os.listdir(self.sound_base):
            os.makedirs(os.path.join(self.sound_base, str(ctx.guild.id)))

        cfg_sounds = await self.config.guild(ctx.guild).sounds()

        if soundname not in cfg_sounds.keys():
            await ctx.send(f'Sound `{soundname}` does not exist. Try `{ctx.prefix}allsfx` for a list.')
            return

        filepath = os.path.join(self.sound_base, str(ctx.guild.id), cfg_sounds[soundname])
        if not os.path.exists(filepath):
            del cfg_sounds[soundname]
            await self.config.guild(ctx.guild).sounds.set(cfg_sounds)
            await ctx.send('Looks like this sound\'s file has gone missing! I\'ve removed it from the list of sounds.')
            return

        await ctx.send(file=discord.File(filepath))

    async def _play_sfx(self, vc, filepath, is_tts=False):
        player = await lavalink.connect(vc)
        track = (await player.get_tracks(query=filepath))[0]

        if player.current is None:
            player.queue.append(track)
            self.current_sfx = (track, is_tts)
            await player.play()
            return

        if self.current_sfx is not None:
            player.queue.insert(0, track)
            await player.skip()
            if self.current_sfx[1]:
                os.remove(self.current_sfx[0].uri)
            self.current_sfx = (track, is_tts)
            return

        self.last_track_info = (player.current, player.position)
        self.current_sfx = (track, is_tts)
        player.queue.insert(0, track)
        player.queue.insert(1, player.current)
        await player.skip()

    async def ll_check(self, player, event, reason):
        if self.current_sfx is None and self.last_track_info is None:
            return

        if event == lavalink.LavalinkEvents.TRACK_EXCEPTION and self.current_sfx is not None:
            if self.current_sfx[1]:
                os.remove(self.current_sfx[0].uri)
            self.current_sfx = None
            return

        if event == lavalink.LavalinkEvents.TRACK_STUCK and self.current_sfx is not None:
            if self.current_sfx[1]:
                os.remove(self.current_sfx[0].uri)
            self.current_sfx = None
            await player.skip()
            return

        if event == lavalink.LavalinkEvents.TRACK_END and player.current is None and self.current_sfx is not None:
            if self.current_sfx[1]:
                os.remove(self.current_sfx[0].uri)
            self.current_sfx = None
            return

        if event == lavalink.LavalinkEvents.TRACK_END and player.current.track_identifier == self.last_track_info[0].track_identifier:
            print(str(self.last_track_info[0].uri))
            if self.current_sfx[1]:
                os.remove(self.current_sfx[0].uri)
            self.current_sfx = None
            await player.pause()
            await player.seek(self.last_track_info[1])
            await player.pause(False)
            self.last_track_info = None<|endoftext|>


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

import os
import sys
import urllib
import urllib2
import json
import difflib
import ConfigParser
from itertools import ifilter
from functools import partial

AREAS_URL = u'http://marknad.sgsstudentbostader.se/API/Service/SearchServiceHandler.ashx?objectMainGroupNo=1&visibleClient=true&visibleProfile=true&asTree=true&Method=GetSearchAreas'

SEARCH_URL = u'http://marknad.sgsstudentbostader.se/API/Service/SearchServiceHandler.ashx'

ITEMINFO_URL = u'http://marknad.sgsstudentbostader.se/pgObjectInformation.aspx?company=1&obj={ObjectNo}'

SEARCH_PARAMS = {
    u"Parm1": u'{"CompanyNo":1,"SyndicateNo":1,"ObjectMainGroupNo":1,"Advertisements":[{"No":-1}],"RentLimit":{"Min":0,"Max":15000},"AreaLimit":{"Min":0,"Max":150},"Page":1,"Take":10,"SortOrder":"EndPeriodMP asc, CompanyNo asc,SeekAreaDescription asc,StreetName asc","ReturnParameters":["ObjectNo","FirstEstateImageUrl","Street","SeekAreaDescription","PlaceName","ObjectSubDescription","ObjectArea","RentPerMonth","MarketPlaceDescription","CountInterest","FirstInfoTextShort","FirstInfoText","EndPeriodMP","FreeFrom","SeekAreaUrl","Latitude","Longitude","BoardNo"]}',
    u"CallbackMethod": u'PostObjectSearch',
    u"CallbackParmCount": u'1',
    u"__WWEVENTCALLBACK": u'' }

SEARCH_HEADERS = {
    u"Accept": u'application/json,text/*',
    u"X-Momentum-API-KEY": u'u15fJ8yRMCIu////+aEYR7+XJwj1hiE9gIXfoo/eje4=',
    u"X-Requested-With": u'XMLHttpRequest' }

OBJECT_FORMAT = u"  {FreeFrom} - {Street} vån. {ObjectFloor} " \
                u"({ObjectAreaSort} kvm, {RentPerMonthSort} kr): " \
                u"{CountInterest} interested."

def fetch_search_data():
    u"""Hämta sökresultat. Returnerar file-like."""
    request = urllib2.Request(SEARCH_URL,
                              urllib.urlencode(SEARCH_PARAMS),
                              SEARCH_HEADERS)
    return urllib2.urlopen(request)

def load_search_data(fp):
    u"""Generöst namngiven metod."""
    data = json.load(fp)
    for obj in data[u"Result"]:
        obj[u"ObjectFloor"] = obj[u"ObjectFloor"].strip()
    return data

def fetch_areas():
    u"""Hämta hem lista på områden. Returnerar file-like."""
    request = urllib2.Request(AREAS_URL, headers=SEARCH_HEADERS)
    return urllib2.urlopen(request)

def load_areas(fp):
    u"""Gör sans av den mottagna areadatan."""
    # dubbelt escapead json. enterprise!
    return get_areas_list(json.loads(json.load(fp)))

def get_areas_list(data, parentname=''):
    u"""Omvandla till mindre obtust format."""
    if data[u"Childs"] == []:
        return [{u"Id": data[u"Id"],
                 u"Description": data[u"Description"],
                 u"Area": parentname}]
    res = []
    for child in data[u"Childs"]:
        res.extend(get_areas_list(child, data[u"Description"]))
    return res

def lookup_areas(userlist):
    u"""Hämta lista på alla områden, kolla upp de av användaren angivna
        områdesnamnen. Tar lista med strängar,
        returnerar lista med {Id, Description, Area}."""
    print u"Desired areas: "
    for userarea in userlist:
        print u"  " + userarea
    print u"Fetching list of all areas...",
    try:
        areas_fp = fetch_areas()
        print "OK"
    except urllib2.URLError as e:
        sys.stderr.write(u"ERROR: failed fetching list of all areas\n")
        raise
    print u"Matching given areas..."
    all_areas = load_areas(fetch_areas())
    res = []
    idres = []
    matcher = difflib.SequenceMatcher()
    for userarea in userlist:
        best_ratio = 0
        best_match = {}
        matcher.set_seq2(userarea)
        it = iter(all_areas)
        while True:
            try:
                area = it.next()
                matcher.set_seq1(area[u"Description"])
                ratio = matcher.ratio()
                if ratio > best_ratio:
                    best_ratio = ratio
                    best_match = area
                if area[u"Description"] == userarea:
                    res.append(area)
                    idres.append(area[u"Id"])
                    break
            except StopIteration:
                sys.stderr.write((u"  WARNING: Couldn't find any match " \
                                  u"for \"{0}\". Did you mean " \
                                  u"\"{1[Description]}\"?\n").format(
                                    userarea, best_match))
                break
    if len(res) < len(userlist):
        sys.stderr.write(u"  WARNING: Some given areas couldn't be matched.\n")
    else:
        print u"All areas matched OK"
    return (res, idres)

def load_config():
    res = {}
    config = ConfigParser.ConfigParser()
    try:
        config.readfp(open(u'sgskoll.conf'))
    except IOError:
        #TODO: default config
        pass
    with open(u'desired_areas.conf') as areafile:
        #TODO: default config
        config.set(u"Search Preferences", u"desired_areas",
                   map(lambda s: s.decode('utf-8'),
                       areafile.read().splitlines()))
    return config

def load_search_prefs(config):
    res = {}
    for key, val in config.items(u"Search Preferences"):
        res[key] = val
    for key in [u"min_rent",u"max_rent",u"min_area",u"max_area"]:
        res[key] = int(res[key])
    res[u"apartment_types"] = map(int, res[u"apartment_types"].split(u","))
    res[u"areas"], res[u"area_ids"] = lookup_areas(res[u"desired_areas"])
    return res

def filterfn(search_prefs, obj):
    return (obj[u"RentPerMonthSort"] >= search_prefs[u"min_rent"] and
            obj[u"RentPerMonthSort"] <= search_prefs[u"max_rent"] and
            obj[u"ObjectAreaSort"] >= search_prefs[u"min_area"] and
            obj[u"ObjectAreaSort"] <= search_prefs[u"max_area"] and
            obj[u"ObjectSubGroupNo"] in search_prefs[u"apartment_types"] and
            obj[u"SeekAreaNo"] in search_prefs[u"area_ids"])

def obj_format_string(obj):
    area_rent = u"{ObjectAreaSort} kvm, {RentPerMonthSort} kr"
    for prop in obj[u"Properties"]:
        if prop[u"SyndicatePropertyNo"] == 5: # 10-mån
            area_rent = area_rent + u" [10 mån]"
            break
    return u"  {FreeFrom} - {Street} vån. {ObjectFloor} " \
           u"(" + area_rent + u"): " \
           u"{CountInterest} interested."

if __name__ == u'__main__':
    print u"Loading config..."
    conf = load_config()
    search_prefs = load_search_prefs(conf)
    print u"Using the following desired areas:"
    for area in search_prefs[u"areas"]:
        print u"  {Id}: {Area}: {Description}".format(**area)
    print u"Preferences: rent in [{min_rent},{max_rent}] kr, " \
          u"area in [{min_area},{max_area}] kvm".format(**search_prefs)

    if not os.path.exists(u'sampledata'):
        print u"Downloading search data..."
        with open(u'sampledata', u'w') as f:
            f.write(fetch_search_data().read())
    else:
        print u"Using previously downloaded data..."

    data = load_search_data(open(u'sampledata'))

    print u"" # lol
    print (u"Most wanted is in {SeekAreaDescription}:\n" +
           OBJECT_FORMAT).format(**max(data[u"Result"],
                                   key=lambda o: o["CountInterest"]))

    print u""
    print u"Found the following matches:"

    for obj in ifilter(partial(filterfn, search_prefs), data[u"Result"]):
        print obj_format_string(obj).format(**obj)
        print u"    " + ITEMINFO_URL.format(**obj)

<commit_msg>program now insults user if no matches were found<commit_after>#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import os
import sys
import urllib
import urllib2
import json
import difflib
import ConfigParser
from itertools import ifilter
from functools import partial

AREAS_URL = u'http://marknad.sgsstudentbostader.se/API/Service/SearchServiceHandler.ashx?objectMainGroupNo=1&visibleClient=true&visibleProfile=true&asTree=true&Method=GetSearchAreas'

SEARCH_URL = u'http://marknad.sgsstudentbostader.se/API/Service/SearchServiceHandler.ashx'

ITEMINFO_URL = u'http://marknad.sgsstudentbostader.se/pgObjectInformation.aspx?company=1&obj={ObjectNo}'

SEARCH_PARAMS = {
    u"Parm1": u'{"CompanyNo":1,"SyndicateNo":1,"ObjectMainGroupNo":1,"Advertisements":[{"No":-1}],"RentLimit":{"Min":0,"Max":15000},"AreaLimit":{"Min":0,"Max":150},"Page":1,"Take":10,"SortOrder":"EndPeriodMP asc, CompanyNo asc,SeekAreaDescription asc,StreetName asc","ReturnParameters":["ObjectNo","FirstEstateImageUrl","Street","SeekAreaDescription","PlaceName","ObjectSubDescription","ObjectArea","RentPerMonth","MarketPlaceDescription","CountInterest","FirstInfoTextShort","FirstInfoText","EndPeriodMP","FreeFrom","SeekAreaUrl","Latitude","Longitude","BoardNo"]}',
    u"CallbackMethod": u'PostObjectSearch',
    u"CallbackParmCount": u'1',
    u"__WWEVENTCALLBACK": u'' }

SEARCH_HEADERS = {
    u"Accept": u'application/json,text/*',
    u"X-Momentum-API-KEY": u'u15fJ8yRMCIu////+aEYR7+XJwj1hiE9gIXfoo/eje4=',
    u"X-Requested-With": u'XMLHttpRequest' }

OBJECT_FORMAT = u"  {FreeFrom} - {Street} vån. {ObjectFloor} " \
                u"({ObjectAreaSort} kvm, {RentPerMonthSort} kr): " \
                u"{CountInterest} interested."

def fetch_search_data():
    u"""Hämta sökresultat. Returnerar file-like."""
    request = urllib2.Request(SEARCH_URL,
                              urllib.urlencode(SEARCH_PARAMS),
                              SEARCH_HEADERS)
    return urllib2.urlopen(request)

def load_search_data(fp):
    u"""Generöst namngiven metod."""
    data = json.load(fp)
    for obj in data[u"Result"]:
        obj[u"ObjectFloor"] = obj[u"ObjectFloor"].strip()
    return data

def fetch_areas():
    u"""Hämta hem lista på områden. Returnerar file-like."""
    request = urllib2.Request(AREAS_URL, headers=SEARCH_HEADERS)
    return urllib2.urlopen(request)

def load_areas(fp):
    u"""Gör sans av den mottagna areadatan."""
    # dubbelt escapead json. enterprise!
    return get_areas_list(json.loads(json.load(fp)))

def get_areas_list(data, parentname=''):
    u"""Omvandla till mindre obtust format."""
    if data[u"Childs"] == []:
        return [{u"Id": data[u"Id"],
                 u"Description": data[u"Description"],
                 u"Area": parentname}]
    res = []
    for child in data[u"Childs"]:
        res.extend(get_areas_list(child, data[u"Description"]))
    return res

def lookup_areas(userlist):
    u"""Hämta lista på alla områden, kolla upp de av användaren angivna
        områdesnamnen. Tar lista med strängar,
        returnerar lista med {Id, Description, Area}."""
    print u"Desired areas: "
    for userarea in userlist:
        print u"  " + userarea
    print u"Fetching list of all areas...",
    try:
        areas_fp = fetch_areas()
        print "OK"
    except urllib2.URLError as e:
        sys.stderr.write(u"ERROR: failed fetching list of all areas\n")
        raise
    print u"Matching given areas..."
    all_areas = load_areas(fetch_areas())
    res = []
    idres = []
    matcher = difflib.SequenceMatcher()
    for userarea in userlist:
        best_ratio = 0
        best_match = {}
        matcher.set_seq2(userarea)
        it = iter(all_areas)
        while True:
            try:
                area = it.next()
                matcher.set_seq1(area[u"Description"])
                ratio = matcher.ratio()
                if ratio > best_ratio:
                    best_ratio = ratio
                    best_match = area
                if area[u"Description"] == userarea:
                    res.append(area)
                    idres.append(area[u"Id"])
                    break
            except StopIteration:
                sys.stderr.write((u"  WARNING: Couldn't find any match " \
                                  u"for \"{0}\". Did you mean " \
                                  u"\"{1[Description]}\"?\n").format(
                                    userarea, best_match))
                break
    if len(res) < len(userlist):
        sys.stderr.write(u"  WARNING: Some given areas couldn't be matched.\n")
    else:
        print u"All areas matched OK"
    return (res, idres)

def load_config():
    res = {}
    config = ConfigParser.ConfigParser()
    try:
        config.readfp(open(u'sgskoll.conf'))
    except IOError:
        #TODO: default config
        pass
    with open(u'desired_areas.conf') as areafile:
        #TODO: default config
        config.set(u"Search Preferences", u"desired_areas",
                   map(lambda s: s.decode('utf-8'),
                       areafile.read().splitlines()))
    return config

def load_search_prefs(config):
    res = {}
    for key, val in config.items(u"Search Preferences"):
        res[key] = val
    for key in [u"min_rent",u"max_rent",u"min_area",u"max_area"]:
        res[key] = int(res[key])
    res[u"apartment_types"] = map(int, res[u"apartment_types"].split(u","))
    res[u"areas"], res[u"area_ids"] = lookup_areas(res[u"desired_areas"])
    return res

def filterfn(search_prefs, obj):
    return (obj[u"RentPerMonthSort"] >= search_prefs[u"min_rent"] and
            obj[u"RentPerMonthSort"] <= search_prefs[u"max_rent"] and
            obj[u"ObjectAreaSort"] >= search_prefs[u"min_area"] and
            obj[u"ObjectAreaSort"] <= search_prefs[u"max_area"] and
            obj[u"ObjectSubGroupNo"] in search_prefs[u"apartment_types"] and
            obj[u"SeekAreaNo"] in search_prefs[u"area_ids"])

def obj_format_string(obj):
    area_rent = u"{ObjectAreaSort} kvm, {RentPerMonthSort} kr"
    for prop in obj[u"Properties"]:
        if prop[u"SyndicatePropertyNo"] == 5: # 10-mån
            area_rent = area_rent + u" [10 mån]"
            break
    return u"  {FreeFrom} - {Street} vån. {ObjectFloor} " \
           u"(" + area_rent + u"): " \
           u"{CountInterest} interested."

if __name__ == u'__main__':
    print u"Loading config..."
    conf = load_config()
    search_prefs = load_search_prefs(conf)
    print u"Using the following desired areas:"
    for area in search_prefs[u"areas"]:
        print u"  {Id}: {Area}: {Description}".format(**area)
    print u"Preferences: rent in [{min_rent},{max_rent}] kr, " \
          u"area in [{min_area},{max_area}] kvm".format(**search_prefs)

    if not os.path.exists(u'sampledata'):
        print u"Downloading search data..."
        with open(u'sampledata', u'w') as f:
            f.write(fetch_search_data().read())
    else:
        print u"Using previously downloaded data..."

    data = load_search_data(open(u'sampledata'))

    print u"" # lol
    print (u"Most wanted is in {SeekAreaDescription}:\n" +
           OBJECT_FORMAT).format(**max(data[u"Result"],
                                   key=lambda o: o["CountInterest"]))

    results = filter(partial(filterfn, search_prefs), data[u"Result"])

    print u""
    if not results:
        print u"No matches were found. Maybe you have too high standards."
    else:
        print u"Found the following matches:"
        for obj in results:
            print obj_format_string(obj).format(**obj)
            print u"    " + ITEMINFO_URL.format(**obj)

<|endoftext|>