<commit_before># this is YOUR api key, which you can find/create here:
# https://www.torn.com/preferences.php#tab=api
# DO NOT share your key with anyone
API_KEY = ""

# ignore users in hosp that only have a few minutes left (they can just wait)
MINIMUM_MINUTES_IN_HOSP = 5

# if someone's in hosp overseas, should we still open them up?
# unless you actually go there, you can't help them, so...
INCLUDE_USERS_OVERSEAS = True

# stop running once someone needing help is found
# if false, it'll continue running forever,
# until you manually kill it from the command line via ctrl-c
STOP_AT_FIRST_FOUND = True

# to avoid getting your ip blocked, the torn api doesn't want you making more than 100 requests per minute
# if you're bold, feel free to make requests more frequently
SECONDS_PER_MINUTE = 60
REQUESTS_PER_MINUTE = 100
TIME_PER_REQUEST = SECONDS_PER_MINUTE / REQUESTS_PER_MINUTE

class LookupBase:
    def __init__(self, obj_id):
        self.obj_id = obj_id
class Faction(LookupBase): pass
class User(LookupBase): pass

ThingsToTrack = [
    # TheReformation, check them before any user
    Faction(21687),
    Faction(15151), # Lifeline (can't heal if we're all out lol)

    # these users are next in priority to Reformation
    User(1873683), # Kapten_Klitoris
    User(20228), # ornery
    User(307651), # Atari
    User(1874922), # JussiJernkuuk
    User(2016267), # Kylar
    User(2006415), # Obsession
    User(135825), # Jimmy007
    User(1160609), # Tipsy
    User(2038695), # EXPLOITED
    User(2006124), # tinytown
    User(1366534), # Dilz
    User(666860), # Ikazuchiryu
    User(9944), # zme1004
    User(927610), # UGK_GEE
    User(1563002), # __Franky__
    User(2154292), # Calla
    User(2163874), # Herpes
#][
    Faction(36891), # Flatline
    Faction(11376), # Buttgrass
    Faction(9201), # axiom
    Faction(9953), # The Drunk Tank
    Faction(31312), # Tornado
    Faction(10174), # The Quidnunks HQ
    Faction(11356), # UGK
    Faction(12863), # Noobs
]

# that's it for the config.
# You shouldn't need to go any further in this file

# external modules
import requests

# bulit into python
from datetime import datetime
import sys
import time
import webbrowser

class TornAPI:
    def __init__(self, key):
        self.key = key
        self.last_request = None

    def get(self, uri):
        if self.last_request:
            since_last_request = time.time() - self.last_request
            time_to_sleep = TIME_PER_REQUEST - since_last_request
            time.sleep(time_to_sleep)

        uri = uri.format(self.key)
        content = requests.get(uri)
        self.last_request = time.time()
        return content.json()

API = TornAPI(API_KEY)

def lookup_users(user_ids):
    for user_id in user_ids:
        data = API.get(f"https://api.torn.com/user/{user_id}?selections=profile&key={{0}}")
        yield data

def lookup_faction(faction_id):
    data = API.get(f"https://api.torn.com/faction/{faction_id}?selections=basic&key={{0}}")
    uri = f"https://www.torn.com/factions.php?step=profile&ID={faction_id}"
    print(f"\nSearching through faction: {data['name']} - {uri}")
    members = data["members"]

    yield from lookup_users(members.keys())

def main():
    for obj in ThingsToTrack:
        thing_id = obj.obj_id
        func = lookup_faction
        if isinstance(obj, User):
            func = lookup_users
            thing_id = [thing_id]

        for user in func(thing_id):
            # status is a two element array.
            # The first element will normally be "Okay", and the second is almost always blank
            # For in hosp, the first will be "In hospital for N mins",
            # and the second will be the reason why
            # ie: "Burned in an arson attempt"

            states = user["states"]
            timestamp = states["hospital_timestamp"]
            if not timestamp:
                # not in hosp
                print(".", end="")
            else:
                out_of_hosp = datetime.fromtimestamp(timestamp)
                time_until_out = (out_of_hosp - datetime.now()).seconds
                # convert to minutes
                time_until_out = time_until_out / 60
                if time_until_out <= MINIMUM_MINUTES_IN_HOSP:
                    # currently in hosp, but only for a few more minutes
                    print("#", end="")
                else:
                    if "In hospital" not in user["status"][0]:
                        # in hosp, needs help, but is overseas
                        print("@", end="")
                        if INCLUDE_USERS_OVERSEAS:
                            webbrowser.open(f"https://www.torn.com/profiles.php?XID={user['player_id']}")
                    else:
                        # in hosp, needs help
                        print("!", end="")
                        webbrowser.open(f"https://www.torn.com/profiles.php?XID={user['player_id']}")
                    if STOP_AT_FIRST_FOUND:
                        return True
            sys.stdout.flush()

        if isinstance(obj, Faction):
            print()
    return False

if __name__ == "__main__":
    try:
        running = True
        while running:
            user_found = main()
            if STOP_AT_FIRST_FOUND and user_found:
                running = False
    except KeyboardInterrupt:
        # swallow up ctrl-c for early termination
        print()
        <commit_msg>changing defaults, and simplifying<commit_after># this is YOUR api key, which you can find/create here:
# https://www.torn.com/preferences.php#tab=api
# DO NOT share your key with anyone
API_KEY = ""

# ignore users in hosp that only have a few minutes left (they can just wait)
MINIMUM_MINUTES_IN_HOSP = 30

# if someone's in hosp overseas, should we still open them up?
# unless you actually go there, you can't help them, so...
INCLUDE_USERS_OVERSEAS = False

# stop running once someone needing help is found
# if false, it'll continue running forever,
# until you manually kill it from the command line via ctrl-c
STOP_AT_FIRST_FOUND = False

# to avoid getting your ip blocked, the torn api doesn't want you making more than 100 requests per minute
# if you're bold, feel free to make requests more frequently
SECONDS_PER_MINUTE = 60
REQUESTS_PER_MINUTE = 100
TIME_PER_REQUEST = SECONDS_PER_MINUTE / REQUESTS_PER_MINUTE

class Faction(int): pass
class User(int): pass
ThingsToTrack = [
    # TheReformation, check them before any user
    Faction(21687),
    Faction(15151), # Lifeline (can't heal if we're all out lol)

    # these users are next in priority to Reformation
    User(1873683), # Kapten_Klitoris
    User(20228), # ornery
    User(307651), # Atari
    User(1874922), # JussiJernkuuk
    User(2016267), # Kylar
    User(2006415), # Obsession
    User(135825), # Jimmy007
    User(1160609), # Tipsy
    User(2038695), # EXPLOITED
    User(2006124), # tinytown
    User(1366534), # Dilz
    User(666860), # Ikazuchiryu
    User(9944), # zme1004
    User(927610), # UGK_GEE
    User(1563002), # __Franky__
    User(2154292), # Calla
    User(2163874), # Herpes
#][
    Faction(36891), # Flatline
    Faction(11376), # Buttgrass
    Faction(9201), # axiom
    Faction(9953), # The Drunk Tank
    Faction(31312), # Tornado
    Faction(10174), # The Quidnunks HQ
    Faction(11356), # UGK
    Faction(12863), # Noobs
]

# that's it for the config.
# You shouldn't need to go any further in this file

# external modules
import requests

# bulit into python
from datetime import datetime
import sys
import time
import webbrowser

class TornAPI:
    def __init__(self, key):
        self.key = key
        self.last_request = None

    def get(self, uri):
        if self.last_request:
            since_last_request = time.time() - self.last_request
            time_to_sleep = TIME_PER_REQUEST - since_last_request
            time.sleep(time_to_sleep)

        uri = uri.format(self.key)
        content = requests.get(uri)
        self.last_request = time.time()
        return content.json()

API = TornAPI(API_KEY)

def lookup_users(user_ids):
    for user_id in user_ids:
        data = API.get(f"https://api.torn.com/user/{user_id}?selections=profile&key={{0}}")
        yield data

def lookup_faction(faction_id):
    data = API.get(f"https://api.torn.com/faction/{faction_id}?selections=basic&key={{0}}")
    uri = f"https://www.torn.com/factions.php?step=profile&ID={faction_id}"
    print(f"\nSearching through faction: {data['name']} - {uri}")
    members = data["members"]

    yield from lookup_users(members.keys())

def main():
    for obj in ThingsToTrack:
        func = lookup_faction
        if isinstance(obj, User):
            func = lookup_users
            obj = [obj]

        for user in func(obj):
            # status is a two element array.
            # The first element will normally be "Okay", and the second is almost always blank
            # For in hosp, the first will be "In hospital for N mins",
            # and the second will be the reason why
            # ie: "Burned in an arson attempt"

            states = user["states"]
            timestamp = states["hospital_timestamp"]
            if not timestamp:
                # not in hosp
                print(".", end="")
            else:
                out_of_hosp = datetime.fromtimestamp(timestamp)
                time_until_out = (out_of_hosp - datetime.now()).seconds
                # convert to minutes
                time_until_out = time_until_out / 60
                if time_until_out <= MINIMUM_MINUTES_IN_HOSP:
                    # currently in hosp, but only for a few more minutes
                    print("#", end="")
                else:
                    if "In hospital" not in user["status"][0]:
                        # in hosp, needs help, but is overseas
                        print("@", end="")
                        if INCLUDE_USERS_OVERSEAS:
                            webbrowser.open(f"https://www.torn.com/profiles.php?XID={user['player_id']}")
                    else:
                        # in hosp, needs help
                        print("!", end="")
                        webbrowser.open(f"https://www.torn.com/profiles.php?XID={user['player_id']}")
                    if STOP_AT_FIRST_FOUND:
                        return True
            sys.stdout.flush()

        if isinstance(obj, Faction):
            print()
    return False

if __name__ == "__main__":
    try:
        running = True
        while running:
            user_found = main()
            if STOP_AT_FIRST_FOUND and user_found:
                running = False
    except KeyboardInterrupt:
        # swallow up ctrl-c for early termination
        print()
        <|endoftext|>


<commit_before># coding: utf-8
from .network import Network
from .hash import hash
from .stellarxdr import StellarXDR_pack as Xdr
import base64

class TransactionEnvelope(object):

    def __init__(self, tx, opts=None):
        self.tx = tx
        try:
            self.signatures = opts.get('signatures')
        except:
            self.signatures = []

    def sign(self, *args):
        tx_hash = self.hash()
        for kp in args:
            sig = kp.signDecorated(tx_hash)
            self.signatures.append(sig)

    def hash(self):
        return hash(self.signature_base())

    def signature_base(self):
        network_id = Network.use_testnet_work().network_id()
        tx_type = bytes([Xdr.const.ENVELOPE_TYPE_TX])  # int to bytes
        tx = Xdr.STELLARXDRPacker()
        tx.pack_Transaction(self.tx.to_xdr_object())
        tx = tx.get_buffer()
        return network_id+tx_type+tx

    def to_xdr_object(self):
        return Xdr.types.TransactionEnvelope(self.tx.to_xdr_object(), self.signatures)

    def submit(self):
        te = Xdr.STELLARXDRPacker()
        te.pack_TransactionEnvelope(self.to_xdr_object())
        te = base64.encodebytes(te.get_buffer())
        return te
<commit_msg>fix signature_base which be used for creating txhash fix typo add  detail AttributeError for __init__<commit_after># coding: utf-8
from .network import Network
from .hash import hash
from .stellarxdr import StellarXDR_pack as Xdr
import base64

class TransactionEnvelope(object):

    def __init__(self, tx, opts=None):
        self.tx = tx
        try:
            self.signatures = opts.get('signatures')
        except AttributeError:
            self.signatures = []

    def sign(self, *args):
        tx_hash = self.hash()
        for kp in args:
            sig = kp.sign_decorated(tx_hash)
            self.signatures.append(sig)

    def hash(self):
        return hash(self.signature_base())

    def signature_base(self):
        network_id = Network.use_testnet_work().network_id()
        tx_type = Xdr.STELLARXDRPacker()
        tx_type.pack_EnvelopeType(Xdr.const.ENVELOPE_TYPE_TX)
        tx_type = tx_type.get_buffer()

        tx = Xdr.STELLARXDRPacker()
        tx.pack_Transaction(self.tx.to_xdr_object())
        tx = tx.get_buffer()
        return network_id+tx_type+tx

    def to_xdr_object(self):
        return Xdr.types.TransactionEnvelope(self.tx.to_xdr_object(), self.signatures)

    def submit(self):
        te = Xdr.STELLARXDRPacker()
        te.pack_TransactionEnvelope(self.to_xdr_object())
        te = base64.encodebytes(te.get_buffer())
        return te
<|endoftext|>


<commit_before>

class Response(object):
    """
    Transloadit http Response Object

    :Attributes:
        - data (dict):
            Dictionary representation of the returned JSON data.
        - status_code (int):
            HTTP response status code
        - headers (dict):
            Dictionary representation of the headers returned from the server.

    :Constructor Args:
        - response (<requests.Response>): The bare response object from the requests library.
    """
    def __init__(self, response):
        self._response = response
        self.data = self._response.json()

    @property
    def status_code(self):
        """
        Return the http status code of the request.
        """
        return self._response.status_code

    @property
    def headers(self):
        """
        Return the response headers.
        """
        return self._response.headers

def as_response(func):
    """
    Decorator function that converts the output of a function into an instance
    of the <transloadit.response.Response> class.
    """
    def _wrapper(*args, **kwargs):
        return Response(func(*args, **kwargs))
    return _wrapper
<commit_msg>fix: retain decorated function's docstrings<commit_after>from six import wraps

class Response(object):
    """
    Transloadit http Response Object

    :Attributes:
        - data (dict):
            Dictionary representation of the returned JSON data.
        - status_code (int):
            HTTP response status code
        - headers (dict):
            Dictionary representation of the headers returned from the server.

    :Constructor Args:
        - response (<requests.Response>): The bare response object from the requests library.
    """
    def __init__(self, response):
        self._response = response
        self.data = self._response.json()

    @property
    def status_code(self):
        """
        Return the http status code of the request.
        """
        return self._response.status_code

    @property
    def headers(self):
        """
        Return the response headers.
        """
        return self._response.headers

def as_response(func):
    """
    Decorator function that converts the output of a function into an instance
    of the <transloadit.response.Response> class.
    """
    @wraps(func)
    def _wrapper(*args, **kwargs):
        return Response(func(*args, **kwargs))
    return _wrapper
<|endoftext|>


<commit_before>try:
    import unittest2 as unittest
except ImportError:
    import unittest # noqa
from cassandra import InvalidRequest

from cassandra.cluster import Cluster
from cassandra.query import PreparedStatement

class PreparedStatementTests(unittest.TestCase):

    def test_basic(self):
        """
        Test basic PreparedStatement usage
        """

        cluster = Cluster()
        session = cluster.connect()
        session.execute(
            """
            CREATE KEYSPACE preparedtests
            WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}
            """)

        session.set_keyspace("preparedtests")
        session.execute(
            """
            CREATE TABLE cf0 (
                a text,
                b text,
                c text,
                PRIMARY KEY (a, b)
            )
            """)

        prepared = session.prepare(
            """
            INSERT INTO cf0 (a, b, c) VALUES  (?, ?, ?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        bound = prepared.bind(('a', 'b', 'c'))

        session.execute(bound)

        prepared = session.prepare(
           """
           SELECT * FROM cf0 WHERE a=?
           """)
        self.assertIsInstance(prepared, PreparedStatement)

        bound = prepared.bind(('a'))
        results = session.execute(bound)
        self.assertEquals(results, [('a', 'b', 'c')])

    def test_missing_primary_key(self):
        """
        Ensure an InvalidRequest is thrown
        when prepared statements are missing the primary key
        """

        cluster = Cluster()
        session = cluster.connect()

        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (v) VALUES  (?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        bound = prepared.bind((1,))
        self.assertRaises(InvalidRequest, session.execute, bound)

    def test_too_many_bind_values(self):
        """
        Ensure a ValueError is thrown when attempting to bind too many variables
        """

        cluster = Cluster()
        session = cluster.connect()

        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (v) VALUES  (?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        self.assertRaises(ValueError, prepared.bind, (1,2))

    def test_none_values(self):
        """
        Ensure binding None is handled correctly
        """

        cluster = Cluster()
        session = cluster.connect()

        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (k, v) VALUES  (?, ?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        bound = prepared.bind((1, None))
        session.execute(bound)

        prepared = session.prepare(
           """
           SELECT * FROM test3rf.test WHERE k=?
           """)
        self.assertIsInstance(prepared, PreparedStatement)

        bound = prepared.bind((1,))
        results = session.execute(bound)
        self.assertEquals(results[0].v, None)

    def test_async_binding(self):
        """
        Ensure None binding over async queries
        """

        cluster = Cluster()
        session = cluster.connect()

        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (k, v) VALUES  (?, ?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        future = session.execute_async(prepared, (873, None))
        future.result()

        prepared = session.prepare(
           """
           SELECT * FROM test3rf.test WHERE k=?
           """)
        self.assertIsInstance(prepared, PreparedStatement)

        future = session.execute_async(prepared, (873,))
        results = future.result()
        self.assertEquals(results[0].v, None)
<commit_msg>Test cases for binding dicts<commit_after>try:
    import unittest2 as unittest
except ImportError:
    import unittest # noqa
from cassandra import InvalidRequest

from cassandra.cluster import Cluster
from cassandra.query import PreparedStatement

class PreparedStatementTests(unittest.TestCase):

    def test_basic(self):
        """
        Test basic PreparedStatement usage
        """

        cluster = Cluster()
        session = cluster.connect()
        session.execute(
            """
            CREATE KEYSPACE preparedtests
            WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}
            """)

        session.set_keyspace("preparedtests")
        session.execute(
            """
            CREATE TABLE cf0 (
                a text,
                b text,
                c text,
                PRIMARY KEY (a, b)
            )
            """)

        prepared = session.prepare(
            """
            INSERT INTO cf0 (a, b, c) VALUES  (?, ?, ?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        bound = prepared.bind(('a', 'b', 'c'))

        session.execute(bound)

        prepared = session.prepare(
           """
           SELECT * FROM cf0 WHERE a=?
           """)
        self.assertIsInstance(prepared, PreparedStatement)

        bound = prepared.bind(('a'))
        results = session.execute(bound)
        self.assertEquals(results, [('a', 'b', 'c')])

        # test with new dict binding
        prepared = session.prepare(
            """
            INSERT INTO cf0 (a, b, c) VALUES  (?, ?, ?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        bound = prepared.bind({
            'a': 'x',
            'b': 'y',
            'c': 'z'
        })

        session.execute(bound)

        prepared = session.prepare(
           """
           SELECT * FROM cf0 WHERE a=?
           """)

        self.assertIsInstance(prepared, PreparedStatement)

        bound = prepared.bind({'a': 'x'})
        results = session.execute(bound)
        self.assertEquals(results, [('x', 'y', 'z')])

    def test_missing_primary_key(self):
        """
        Ensure an InvalidRequest is thrown
        when prepared statements are missing the primary key
        """

        cluster = Cluster()
        session = cluster.connect()

        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (v) VALUES  (?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        bound = prepared.bind((1,))
        self.assertRaises(InvalidRequest, session.execute, bound)

    def test_missing_primary_key_dicts(self):
        """
        Ensure an InvalidRequest is thrown
        when prepared statements are missing the primary key
        with dict bindings
        """

        cluster = Cluster()
        session = cluster.connect()

        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (v) VALUES  (?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        bound = prepared.bind({'v': 1})
        self.assertRaises(InvalidRequest, session.execute, bound)

    def test_too_many_bind_values(self):
        """
        Ensure a ValueError is thrown when attempting to bind too many variables
        """

        cluster = Cluster()
        session = cluster.connect()

        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (v) VALUES  (?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        self.assertRaises(ValueError, prepared.bind, (1,2))

    def test_too_many_bind_values_dicts(self):
        """
        Ensure a ValueError is thrown when attempting to bind too many variables
        with dict bindings
        """

        cluster = Cluster()
        session = cluster.connect()

        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (v) VALUES  (?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        self.assertRaises(ValueError, prepared.bind, {'k': 1, 'v': 2})

        # also catch too few variables with dicts
        self.assertIsInstance(prepared, PreparedStatement)
        self.assertRaises(KeyError, prepared.bind, {})

    def test_none_values(self):
        """
        Ensure binding None is handled correctly
        """

        cluster = Cluster()
        session = cluster.connect()

        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (k, v) VALUES  (?, ?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        bound = prepared.bind((1, None))
        session.execute(bound)

        prepared = session.prepare(
           """
           SELECT * FROM test3rf.test WHERE k=?
           """)
        self.assertIsInstance(prepared, PreparedStatement)

        bound = prepared.bind((1,))
        results = session.execute(bound)
        self.assertEquals(results[0].v, None)

    def test_none_values_dicts(self):
        """
        Ensure binding None is handled correctly with dict bindings
        """

        cluster = Cluster()
        session = cluster.connect()

        # test with new dict binding
        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (k, v) VALUES  (?, ?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        bound = prepared.bind({'k': 1, 'v': None})
        session.execute(bound)

        prepared = session.prepare(
           """
           SELECT * FROM test3rf.test WHERE k=?
           """)
        self.assertIsInstance(prepared, PreparedStatement)

        bound = prepared.bind({'k': 1})
        results = session.execute(bound)
        self.assertEquals(results[0].v, None)

    def test_async_binding(self):
        """
        Ensure None binding over async queries
        """

        cluster = Cluster()
        session = cluster.connect()

        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (k, v) VALUES  (?, ?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        future = session.execute_async(prepared, (873, None))
        future.result()

        prepared = session.prepare(
           """
           SELECT * FROM test3rf.test WHERE k=?
           """)
        self.assertIsInstance(prepared, PreparedStatement)

        future = session.execute_async(prepared, (873,))
        results = future.result()
        self.assertEquals(results[0].v, None)

    def test_async_binding_dicts(self):
        """
        Ensure None binding over async queries with dict bindings
        """

        cluster = Cluster()
        session = cluster.connect()

        prepared = session.prepare(
            """
            INSERT INTO test3rf.test (k, v) VALUES  (?, ?)
            """)

        self.assertIsInstance(prepared, PreparedStatement)
        future = session.execute_async(prepared, {'k': 873, 'v': None})
        future.result()

        prepared = session.prepare(
           """
           SELECT * FROM test3rf.test WHERE k=?
           """)
        self.assertIsInstance(prepared, PreparedStatement)

        future = session.execute_async(prepared, {'k': 873})
        results = future.result()
        self.assertEquals(results[0].v, None)
<|endoftext|>


<commit_before>from django.utils.translation import ugettext_lazy as _

class Base(object):
    """
    Base availability policy.
    """

    #: Availability code.  This is used for HTML classes
    code = ''

    #: A description of the availability of a product.  This is shown on the
    #: product detail page.  Eg "In stock", "Out of stock" etc
    message = ''

    #: When this item should be dispatched
    dispatch_date = None

    @property
    def short_message(self):
        """
        A shorter version of the availability message, suitable for showing on
        browsing pages.
        """
        return self.message

    @property
    def is_available_to_buy(self):
        """
        Test if this product is available to be bought.  This is used for
        validation when a product is added to a user's basket.
        """
        # We test a purchase of a single item
        return self.is_purchase_permitted(1)[0]

    def is_purchase_permitted(self, quantity):
        """
        Test whether a proposed purchase is allowed

        Should return a boolean and a reason
        """
        return False, _("unavailable")

# Common availability policies

class Unavailable(Base):
    """
    Policy for when a product is unavailable
    """
    code = 'unavailable'
    message = _("Unavailable")

class Available(Base):
    """
    For when a product is always available, irrespective of stock level.

    This might be appropriate for digital products where stock doesn't need to
    be tracked and the product is always available to buy.
    """
    code = 'available'
    message = _("Available")

    def is_purchase_permitted(self, quantity):
        return True, ""

class StockRequired(Base):
    """
    Allow a product to be bought while there is stock.  This policy is
    instantiated with a stock number (``num_available``).  It ensures that the
    product is only available to buy while there is stock available.

    This is suitable for physical products where back orders (eg allowing
    purchases when there isn't stock available) are not permitted.
    """
    CODE_IN_STOCK = 'instock'
    CODE_OUT_OF_STOCK = 'outofstock'

    def __init__(self, num_available):
        self.num_available = num_available

    def is_purchase_permitted(self, quantity):
        if self.num_available == 0:
            return False, _("no stock available")
        if quantity > self.num_available:
            msg = _("a maximum of %(max)d can be bought") % {
                'max': self.num_available}
            return False, msg
        return True, ""

    @property
    def code(self):
        if self.num_available > 0:
            return self.CODE_IN_STOCK
        return self.CODE_OUT_OF_STOCK

    @property
    def short_message(self):
        if self.num_available > 0:
            return _("In stock")
        return _("Unavailable")

    @property
    def message(self):
        if self.num_available > 0:
            return _("In stock (%d available)") % self.num_available
        return _("Unavailable")
<commit_msg>Move availability<commit_after><|endoftext|>


<commit_before>import itertools

from django import template
from django.conf import settings
from channelguide import util

register = template.Library()

def quoted_attribute(attr):
    try:
        return attr[0] == attr[-1] == '"'
    except IndexError:
        return False

def unquote_attribute(attr):
    return attr[1:-1]

def make_text_or_variable_node(parser, expression):
    if quoted_attribute(expression):
        return template.TextNode(unquote_attribute(expression))
    else:
        return template.VariableNode(parser.compile_filter(expression))

def do_generic_button(parser, token, endtag, *css_classes):
    tokens = token.split_contents()
    nodelist = parser.parse((endtag, ))
    parser.delete_first_token()
    url_node = make_text_or_variable_node(parser, tokens[1])
    return ButtonNode(css_classes, url_node, nodelist)

@register.tag('whitebutton')
def do_white_button(parser, token):
    return do_generic_button(parser, token, 'endwhitebutton',
        "white-button-left", "white-button-right", "white-button-content")

@register.tag('homebutton')
def do_home_button(parser, token):
    return do_generic_button(parser, token, 'endhomebutton',
        "home-button-left", "home-button-right", "home-button-content")

class ButtonNode(template.Node):
    def __init__(self, div_classes, url_node, nodelist):
        self.start_html = self.make_start_html(div_classes)
        self.end_html = self.make_end_html(div_classes)
        self.link_class = div_classes[0]
        self.url_node = url_node
        self.nodelist = nodelist

    def make_start_html(self, div_classes):
        output = []
        output.append('')
        for other_class in div_classes[1:]:
            output.append('' % other_class)
        return ''.join(output)

    def make_end_html(self, div_classes):
        output = []
        for i in xrange(len(div_classes) - 1):
            output.append('')
        output.append('')
        return ''.join(output)

    def render(self, context):
        relative_url = self.url_node.render(context)
        link_attrs = util.make_link_attributes(relative_url, self.link_class)
        start_html = self.start_html % link_attrs
        inner_html = self.nodelist.render(context)
        return '%s%s%s' % (start_html, inner_html, self.end_html)

@register.tag('link')
def do_link(parser, token):
    tokens = token.split_contents()
    syntax_msg = 'syntax is link <relative_link> ["css-class"]'
    if len(tokens) not in (2, 3):
        raise template.TemplateSyntaxError(syntax_msg)
    relative_link = tokens[1]
    try:
        css_class = tokens[2]
        if not quoted_attribute(css_class):
            raise template.TemplateSyntaxError(syntax_msg)
        css_class = unquote_attribute(css_class)
    except IndexError:
        css_class = None

    url_node = make_text_or_variable_node(parser, relative_link)
    return LinkNode(url_node, css_class)

class LinkNode(template.Node):
    def __init__(self, url_node, css_class):
        self.url_node = url_node
        self.css_class = css_class

    def render(self, context):
        relative_url = self.url_node.render(context)
        return util.make_link_attributes(relative_url, self.css_class)

@register.tag('ajaxlink')
def do_link(parser, token):
    tokens = token.split_contents()
    syntax_msg = 'syntax is link <ajax-url> "node-id" <fallback-url> ["css-class"]'
    if len(tokens) not in (4, 5):
        raise template.TemplateSyntaxError(syntax_msg)
    ajax_url_node = make_text_or_variable_node(parser, tokens[1])
    fallback_url_node = make_text_or_variable_node(parser, tokens[3])
    try:
        css_class = tokens[4]
    except IndexError:
        css_class = None
    else:
        if not quoted_attribute(css_class):
            raise template.TemplateSyntaxError(syntax_msg)
        css_class = unquote_attribute(css_class)
    node_id = tokens[2]
    if not quoted_attribute(node_id):
        raise template.TemplateSyntaxError(syntax_msg)
    node_id = unquote_attribute(node_id)

    return AjaxLinkNode(ajax_url_node, node_id, fallback_url_node, css_class)

class AjaxLinkNode(template.Node):
    def __init__(self, ajax_url_node, node_id, fallback_url_node, css_class):
        self.ajax_url_node = ajax_url_node
        self.node_id = node_id
        self.fallback_url_node = fallback_url_node
        self.css_class = css_class

    def render(self, context):
        ajax_url = self.ajax_url_node.render(context)
        fallback_url = self.fallback_url_node.render(context)
        onclick = "return ajaxLink('%s', '%s');" % (ajax_url, self.node_id)
        return util.make_link_attributes(fallback_url, self.css_class,
                onclick=onclick)

@register.tag('navlink')
def do_navlink(parser, token):
    tokens = token.split_contents()
    if len(tokens) != 2 or not quoted_attribute(tokens[1]):
        msg = 'syntax is navlink "<relative_link_path>"'
        raise template.TemplateSyntaxError(msg)
    return NavLinkNode(unquote_attribute(tokens[1]))

class NavLinkNode(template.Node):
    def __init__(self, relative_path):
        self.path = settings.BASE_URL_PATH + relative_path
        self.relative_path = relative_path

    def render(self, context):
        try:
            request_path = context['request'].path
        except KeyError:
            request_path = None
        if self.path == request_path:
            css_class = 'current-page'
        else:
            css_class = None
        return util.make_link_attributes(self.relative_path, css_class)

@register.inclusion_tag('guide/formbutton.html')
def formbutton(url, action, label=None):
    if label is None:
        label = action
    return {'url': url, 'action': action, 'label': label}

@register.inclusion_tag('guide/account-bar.html')
def show_account_bar(user):
    return {'user': user}

@register.inclusion_tag('guide/form.html')
def show_form(form):
    return {'form': form, 'BASE_URL': settings.BASE_URL }

@register.inclusion_tag('guide/form-errors.html')
def show_form_errors(form):
    return {'form': form }

@register.inclusion_tag('guide/form-field.html')
def show_form_field(field):
    return {'field': field, 'BASE_URL': settings.BASE_URL }

@register.inclusion_tag('guide/pager.html')
def show_pager(pager):
    return {'pager': pager}

@register.inclusion_tag('guide/view-select.html')
def show_view_select(view_select):
    return {'view_select': view_select}

@register.tag(name='twocolumns')
def do_twocolumns(parser, token):
    first_column = parser.parse(('splitcolumns',))
    parser.delete_first_token()
    second_column = parser.parse(('endtwocolumns',))
    parser.delete_first_token()
    return TwoColumnNode(first_column, second_column)

class TwoColumnNode(template.Node):
    def __init__(self, first_column, second_column):
        self.columns = (first_column, second_column)

    def render(self, context):
        output = []
        output.append('
')
        output.append('
')
        output.append(self.columns[0].render(context))
        output.append('

')
        output.append(self.columns[1].render(context))
        output.append('

')
        return ''.join(output)

@register.tag(name='threecolumns')
def do_threecolumns(parser, token):
    first_column = parser.parse(('splitcolumns',))
    parser.delete_first_token()
    second_column = parser.parse(('splitcolumns',))
    parser.delete_first_token()
    third_column = parser.parse(('endthreecolumns',))
    parser.delete_first_token()
    return ThreeColumnNode(first_column, second_column, third_column)

class ThreeColumnNode(template.Node):
    def __init__(self, first_column, second_column, third_column):
        self.columns = (first_column, second_column, third_column)

    def render(self, context):
        output = []
        output.append('
')
        output.append('
')
        output.append(self.columns[0].render(context))
        output.append('

')
        output.append(self.columns[1].render(context))
        output.append('

')
        output.append(self.columns[2].render(context))
        output.append('

')
        return ''.join(output)

def parse_column_loop(parser, token, deliminator):
    tokens = token.split_contents()
    syntax_msg = "syntax is twocolumnloop with <varname> in <list> [rotated]"
    if tokens[1] != 'with' or tokens[3] != 'in':
        raise template.TemplateSyntaxError(syntax_msg)
    if len(tokens) == 6:
        if tokens[5] == 'rotated':
            rotated = True
        else:
            raise template.TemplateSyntaxError(syntax_msg)
    elif len(tokens) == 5:
        rotated = False
    else:
        raise template.TemplateSyntaxError(syntax_msg)
    item_name = tokens[2]
    list_name = tokens[4]
    nodelist = parser.parse((deliminator,))
    parser.delete_first_token()
    return nodelist, list_name, item_name, rotated

@register.tag(name='twocolumnloop')
def do_twocolumnloop(parser, token):
    columns = ['first-column', 'second-column']
    return ColumnLoopNode(columns, 'two-column-list',
            *parse_column_loop(parser, token, 'endtwocolumnloop'))

@register.tag(name='threecolumnloop')
def do_threecolumnloop(parser, token):
    columns = ['first-column', 'second-column', 'third-column']
    return ColumnLoopNode(columns, 'three-column-list',
            *parse_column_loop(parser, token, 'endthreecolumnloop'))

@register.tag(name='fourcolumnloop')
def do_fourcolumnloop(parser, token):
    columns = ['first-column', 'second-column', 'third-column',
            'fourth-column']
    return ColumnLoopNode(columns, 'four-column-list',
            *parse_column_loop(parser, token, 'endfourcolumnloop'))

class ColumnLoopNode(template.Node):
    def __init__(self, column_names, list_css_class, nodelist, list_name,
            item_name, rotated):
        self.column_names = column_names
        self.list_css_class = list_css_class
        self.width = len(self.column_names)
        self.nodelist = nodelist
        self.list_name = list_name
        self.item_name = item_name
        self.rotated = rotated

    def rotate_grid(self, list):
        pad_out = self.width - (len(list) % self.width)
        if pad_out == self.width:
            pad_out = 0
        list = list + [None] * pad_out

        source_col = 0
        source_rows = len(list) / self.width
        retval = []
        while len(retval) < len(list):
            retval.extend(list[source_col:len(list):source_rows])
            source_col += 1
        return retval

    def render(self, context):
        loop_over = template.resolve_variable(self.list_name, context)
        if not isinstance(loop_over, list):
            loop_over = list(loop_over)
        if self.rotated:
            loop_over = self.rotate_grid(loop_over)
        output = ['
' % self.list_css_class]
        counter = itertools.count()
        for item in loop_over:
            i = counter.next()
            if item is None:
                continue
            context[self.item_name] = item
            css_class = "column %s" % self.column_names[i % self.width]
            if i < self.width:
                css_class += ' first-row'
            if i + self.width >= len(loop_over):
                css_class += ' last-row'
            output.append('
' % css_class)
            output.append(self.nodelist.render(context))
            output.append('
\n')
        output.append('

')
        return ''.join(output)

<commit_msg>use more efficient rendering<commit_after>import itertools

from django import template
from django.conf import settings
from channelguide import util

register = template.Library()

def quoted_attribute(attr):
    try:
        return attr[0] == attr[-1] == '"'
    except IndexError:
        return False

def unquote_attribute(attr):
    return attr[1:-1]

def make_text_or_variable_node(parser, expression):
    if quoted_attribute(expression):
        return template.TextNode(unquote_attribute(expression))
    else:
        return template.VariableNode(parser.compile_filter(expression))

def do_generic_button(parser, token, endtag, *css_classes):
    tokens = token.split_contents()
    nodelist = parser.parse((endtag, ))
    parser.delete_first_token()
    url_node = make_text_or_variable_node(parser, tokens[1])
    return ButtonNode(css_classes, url_node, nodelist)

@register.tag('whitebutton')
def do_white_button(parser, token):
    return do_generic_button(parser, token, 'endwhitebutton',
        "white-button-left", "white-button-right", "white-button-content")

@register.tag('homebutton')
def do_home_button(parser, token):
    return do_generic_button(parser, token, 'endhomebutton',
        "home-button-left", "home-button-right", "home-button-content")

class ButtonNode(template.Node):
    def __init__(self, div_classes, url_node, nodelist):
        self.start_html = self.make_start_html(div_classes)
        self.end_html = self.make_end_html(div_classes)
        self.link_class = div_classes[0]
        self.url_node = url_node
        self.nodelist = nodelist

    def make_start_html(self, div_classes):
        output = []
        output.append('')
        for other_class in div_classes[1:]:
            output.append('' % other_class)
        return ''.join(output)

    def make_end_html(self, div_classes):
        output = []
        for i in xrange(len(div_classes) - 1):
            output.append('')
        output.append('')
        return ''.join(output)

    def render(self, context):
        relative_url = self.url_node.render(context)
        link_attrs = util.make_link_attributes(relative_url, self.link_class)
        start_html = self.start_html % link_attrs
        inner_html = self.nodelist.render(context)
        return '%s%s%s' % (start_html, inner_html, self.end_html)

@register.tag('link')
def do_link(parser, token):
    tokens = token.split_contents()
    syntax_msg = 'syntax is link <relative_link> ["css-class"]'
    if len(tokens) not in (2, 3):
        raise template.TemplateSyntaxError(syntax_msg)
    relative_link = tokens[1]
    try:
        css_class = tokens[2]
        if not quoted_attribute(css_class):
            raise template.TemplateSyntaxError(syntax_msg)
        css_class = unquote_attribute(css_class)
    except IndexError:
        css_class = None

    url_node = make_text_or_variable_node(parser, relative_link)
    return LinkNode(url_node, css_class)

class LinkNode(template.Node):
    def __init__(self, url_node, css_class):
        self.url_node = url_node
        self.css_class = css_class

    def render(self, context):
        relative_url = self.url_node.render(context)
        return util.make_link_attributes(relative_url, self.css_class)

@register.tag('ajaxlink')
def do_link(parser, token):
    tokens = token.split_contents()
    syntax_msg = 'syntax is link <ajax-url> "node-id" <fallback-url> ["css-class"]'
    if len(tokens) not in (4, 5):
        raise template.TemplateSyntaxError(syntax_msg)
    ajax_url_node = make_text_or_variable_node(parser, tokens[1])
    fallback_url_node = make_text_or_variable_node(parser, tokens[3])
    try:
        css_class = tokens[4]
    except IndexError:
        css_class = None
    else:
        if not quoted_attribute(css_class):
            raise template.TemplateSyntaxError(syntax_msg)
        css_class = unquote_attribute(css_class)
    node_id = tokens[2]
    if not quoted_attribute(node_id):
        raise template.TemplateSyntaxError(syntax_msg)
    node_id = unquote_attribute(node_id)

    return AjaxLinkNode(ajax_url_node, node_id, fallback_url_node, css_class)

class AjaxLinkNode(template.Node):
    def __init__(self, ajax_url_node, node_id, fallback_url_node, css_class):
        self.ajax_url_node = ajax_url_node
        self.node_id = node_id
        self.fallback_url_node = fallback_url_node
        self.css_class = css_class

    def render(self, context):
        ajax_url = self.ajax_url_node.render(context)
        fallback_url = self.fallback_url_node.render(context)
        onclick = "return ajaxLink('%s', '%s');" % (ajax_url, self.node_id)
        return util.make_link_attributes(fallback_url, self.css_class,
                onclick=onclick)

@register.tag('navlink')
def do_navlink(parser, token):
    tokens = token.split_contents()
    if len(tokens) != 2 or not quoted_attribute(tokens[1]):
        msg = 'syntax is navlink "<relative_link_path>"'
        raise template.TemplateSyntaxError(msg)
    return NavLinkNode(unquote_attribute(tokens[1]))

class NavLinkNode(template.Node):
    def __init__(self, relative_path):
        self.path = settings.BASE_URL_PATH + relative_path
        self.relative_path = relative_path

    def render(self, context):
        try:
            request_path = context['request'].path
        except KeyError:
            request_path = None
        if self.path == request_path:
            css_class = 'current-page'
        else:
            css_class = None
        return util.make_link_attributes(self.relative_path, css_class)

@register.inclusion_tag('guide/formbutton.html')
def formbutton(url, action, label=None):
    if label is None:
        label = action
    return {'url': url, 'action': action, 'label': label}

@register.inclusion_tag('guide/account-bar.html')
def show_account_bar(user):
    return {'user': user}

@register.inclusion_tag('guide/form.html')
def show_form(form):
    return {'form': form, 'BASE_URL': settings.BASE_URL }

@register.inclusion_tag('guide/form-errors.html')
def show_form_errors(form):
    return {'form': form }

@register.inclusion_tag('guide/form-field.html')
def show_form_field(field):
    return {'field': field, 'BASE_URL': settings.BASE_URL }

@register.inclusion_tag('guide/pager.html')
def show_pager(pager):
    return {'pager': pager}

@register.inclusion_tag('guide/view-select.html')
def show_view_select(view_select):
    return {'view_select': view_select}

@register.tag(name='twocolumns')
def do_twocolumns(parser, token):
    first_column = parser.parse(('splitcolumns',))
    parser.delete_first_token()
    second_column = parser.parse(('endtwocolumns',))
    parser.delete_first_token()
    return TwoColumnNode(first_column, second_column)

class TwoColumnNode(template.Node):
    def __init__(self, first_column, second_column):
        self.columns = (first_column, second_column)

    def render(self, context):
        return """

        %s

        %s

""" % (self.columns[0].render(context),
        self.columns[1].render(context))

@register.tag(name='threecolumns')
def do_threecolumns(parser, token):
    first_column = parser.parse(('splitcolumns',))
    parser.delete_first_token()
    second_column = parser.parse(('splitcolumns',))
    parser.delete_first_token()
    third_column = parser.parse(('endthreecolumns',))
    parser.delete_first_token()
    return ThreeColumnNode(first_column, second_column, third_column)

class ThreeColumnNode(template.Node):
    def __init__(self, first_column, second_column, third_column):
        self.columns = (first_column, second_column, third_column)

    def render(self, context):
        output = []
        output.append('
')
        output.append('
')
        output.append(self.columns[0].render(context))
        output.append('

')
        output.append(self.columns[1].render(context))
        output.append('

')
        output.append(self.columns[2].render(context))
        output.append('

')
        return ''.join(output)

def parse_column_loop(parser, token, deliminator):
    tokens = token.split_contents()
    syntax_msg = "syntax is twocolumnloop with <varname> in <list> [rotated]"
    if tokens[1] != 'with' or tokens[3] != 'in':
        raise template.TemplateSyntaxError(syntax_msg)
    if len(tokens) == 6:
        if tokens[5] == 'rotated':
            rotated = True
        else:
            raise template.TemplateSyntaxError(syntax_msg)
    elif len(tokens) == 5:
        rotated = False
    else:
        raise template.TemplateSyntaxError(syntax_msg)
    item_name = tokens[2]
    list_name = tokens[4]
    nodelist = parser.parse((deliminator,))
    parser.delete_first_token()
    return nodelist, list_name, item_name, rotated

@register.tag(name='twocolumnloop')
def do_twocolumnloop(parser, token):
    columns = ['first-column', 'second-column']
    return ColumnLoopNode(columns, 'two-column-list',
            *parse_column_loop(parser, token, 'endtwocolumnloop'))

@register.tag(name='threecolumnloop')
def do_threecolumnloop(parser, token):
    columns = ['first-column', 'second-column', 'third-column']
    return ColumnLoopNode(columns, 'three-column-list',
            *parse_column_loop(parser, token, 'endthreecolumnloop'))

@register.tag(name='fourcolumnloop')
def do_fourcolumnloop(parser, token):
    columns = ['first-column', 'second-column', 'third-column',
            'fourth-column']
    return ColumnLoopNode(columns, 'four-column-list',
            *parse_column_loop(parser, token, 'endfourcolumnloop'))

class ColumnLoopNode(template.Node):
    def __init__(self, column_names, list_css_class, nodelist, list_name,
            item_name, rotated):
        self.column_names = column_names
        self.list_css_class = list_css_class
        self.width = len(self.column_names)
        self.nodelist = nodelist
        self.list_name = list_name
        self.item_name = item_name
        self.rotated = rotated

    def rotate_grid(self, list):
        pad_out = self.width - (len(list) % self.width)
        if pad_out == self.width:
            pad_out = 0
        list = list + [None] * pad_out

        source_col = 0
        source_rows = len(list) / self.width
        retval = []
        while len(retval) < len(list):
            retval.extend(list[source_col:len(list):source_rows])
            source_col += 1
        return retval

    def render(self, context):
        loop_over = template.resolve_variable(self.list_name, context)
        if not isinstance(loop_over, list):
            loop_over = list(loop_over)
        if self.rotated:
            loop_over = self.rotate_grid(loop_over)
        output = ['
' % self.list_css_class]
        counter = itertools.count()
        for item in loop_over:
            i = counter.next()
            if item is None:
                continue
            context[self.item_name] = item
            css_class = "column %s" % self.column_names[i % self.width]
            if i < self.width:
                css_class += ' first-row'
            if i + self.width >= len(loop_over):
                css_class += ' last-row'
            output.append('
' % css_class)
            output.append(self.nodelist.render(context))
            output.append('
\n')
        output.append('

')
        return ''.join(output)

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from django.conf.urls import include, patterns, url
from django.conf import settings
from web import views
from web import autocomplete_views

urlpatterns = patterns('',
    url(r'^$', views.index, name='index'),
    url(r'^links[/]+$', views.links, name='links'),

    url(r'^cards[/]+$', views.cards, name='cards'),
    url(r'^card[s]?/(?P<card>\d+)[/]$', views.cards, name='cards'),
    url(r'^cards[s]?/(?P<card>\d+)/[\w-]+[/]+$', views.cards, name='cards'),

    url(r'^songs[/]+$', views.songs, name='songs'),
    url(r'^song[s]?/(?P<song>[^/]+)[/]$', views.songs, name='songs'),

    url(r'^create[/]+$', views.create, name='create'),
    url(r'^edit[/]+$', views.edit, name='edit'),
    url(r'^login[/]+$', views.login_custom_view, name='login'),
    url(r'^setaccountonlogin[/]+$', views.setaccountonlogin, name='setaccountonlogin'),
    url(r'^logout[/]+$', 'django.contrib.auth.views.logout', {'next_page': '/'}),
    url(r'^addaccount[/]+$', views.addaccount, name='addaccount'),
    url(r'^editaccount/(?P<account>\d+)[/]+$', views.editaccount, name='editaccount'),
    url(r'^reportaccount/(?P<account>\d+)[/]+$', views.report, name='reportaccount'),
    url(r'^reporteventparticipation/(?P<eventparticipation>\d+)[/]+$', views.report, name='reporteventparticipation'),
    url(r'^reportuser/(?P<user>[\w.@+-]+)[/]+$', views.report, name='reportuser'),
    url(r'^reportactivity/(?P<activity>\d+)[/]+$', views.report, name='reportactivity'),

    url(r'^addteam/(?P<account>\d+)[/]+$', views.addteam, name='addteam'),
    url(r'^editteam/(?P<team>\d+)[/]+$', views.editteam, name='editteam'),
    url(r'^user[s]?/(?P<username>[\w.@+-]+)[/]+$', views.profile, name='profile'),
    url(r'^users[/]+$', views.users, name='users'),
    url(r'^events[/]+$', views.events, name='events'),
    url(r'^event[s]?/(?P<event>[^/]+)[/]+$', views.event, name='event'),
    url(r'^event[s]?/(?P<event>[^/]+)/participations[/]+$', views.eventparticipations, name='eventparticipations'),
    url(r'^idols[/]+$', views.idols, name='idols'),
    url(r'^idol[s]?/(?P<idol>[^/]+)[/]+$', views.idol, name='idol'),
    url(r'^collections[/]+$', views.collections, name='collections'),
    url(r'^collection[s]?/(?P<collection>[^/]+)[/]+$', views.collection, name='collection'),
    url(r'^discussions[/]+$', views.discussions, name='discussions'),
    url(r'^discussion[s]?/(?P<discussion>[\w_]+)[/]$', views.discussion, name='discussion'),
    url(r'^activities/(?P<activity>\d+)[/]+$', views.activity, name='activity'),
    url(r'^map[/]+$', views.mapview, name='map'),
    url(r'^trivia[/]+$', views.trivia, name='trivia'),
    url(r'^about[/]+$', views.aboutview, name='about'),
    url(r'^android[/]+$', views.android, name='android'),
    url(r'^cards/albumbuilder[/]+$', views.albumbuilder, name='albumbuilder'),
    url(r'^cards/initialsetup[/]+$', views.initialsetup, name='initialsetup'),
    url(r'^skillup[/]+$', views.skillup, name='skillup'),
    url(r'^staff/verifications[/]$', views.staff_verifications, name='staff_verifications'),
    url(r'^staff/verification/(?P<verification>\d+)[/]$', views.staff_verification, name='staff_verification'),
    url(r'^staff/reports[/]$', views.staff_reports, name='staff_reports'),
    url(r'^staff/moderation[/]$', views.staff_moderation, name='staff_moderation'),
    url(r'^staff/database[/]$', views.staff_database, name='staff_database'),
    url(r'^staff/database/(?P<script>[\w -_]+)[/]$', views.staff_database_script, name='staff_database_script'),
    url(r'^urpairs[/]+$', views.urpairs, name='urpairs'),
    url(r'^.sicaltriofestival[/]+$', views.usicaltriofestival, name='usicaltriofestival'),

    url(r'^password_reset[/]+$', 'django.contrib.auth.views.password_reset', {
        'template_name': 'password/password_reset_form.html',
        'html_email_template_name': 'password/password_reset_email_html.html',
        'from_email': settings.AWS_PASSWORD_EMAIL,
     }, name='password_reset'),
    url(r'^password_reset/done[/]+$', 'django.contrib.auth.views.password_reset_done', {
        'template_name': 'password/password_reset_done.html'
    }, name='password_reset_done'),
    url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', views.password_reset_confirm, {
        'template_name': 'password/password_reset_confirm.html'
    }, name='password_reset_confirm'),
    url(r'^reset/done[/]+$', 'django.contrib.auth.views.password_reset_complete', {
        'template_name': 'password/password_reset_complete.html'
    }, name='password_reset_complete'),

    url(r'^ajax/modal/(?P<hash>\w+)[/]+$', views.ajaxmodal, name='ajaxmodal'),
    url(r'^ajax/addcard[/]+$', views.ajaxaddcard, name='ajaxaddcard'),
    url(r'^ajax/editcard/(?P<ownedcard>\d+)[/]+$', views.ajaxeditcard, name='ajaxeditcard'),
    url(r'^ajax/deletecard/(?P<ownedcard>\d+)[/]+$', views.ajaxdeletecard, name='ajaxdeletecard'),
    url(r'^ajax/albumbuilder/editcard/(?P<ownedcard_id>\d+)[/]+$', views.ajax_albumbuilder_editcard, name='ajax_albumbuilder_editcard'),
    url(r'^ajax/albumbuilder/addcard/(?P<card_id>\d+)[/]+$', views.ajax_albumbuilder_addcard, name='ajax_albumbuilder_addcard'),
    url(r'^ajax/deletelink/(?P<link>\d+)[/]+$', views.ajaxdeletelink, name='ajaxdeletelink'),
    url(r'^ajax/cards[/]+$', views.ajaxcards, name='ajaxcards'),
    url(r'^ajax/songs[/]+$', views.ajaxsongs, name='ajaxsongs'),
    url(r'^ajax/users[/]+$', views.ajaxusers, name='ajaxusers'),
    url(r'^ajax/accounttab/(?P<account>\d+)/(?P<tab>\w+)[/]+$', views.ajaxaccounttab, name='ajaxaccounttab'),
    url(r'^ajax/accounttab/(?P<account>\d+)/(?P<tab>\w+)/more[/]+$', views.ajaxaccounttab, { 'more': True }, name='ajaxaccounttab'),
    url(r'^ajax/follow/(?P<username>[\w.@+-]+)[/]+$', views.ajaxfollow, name='ajaxfollow'),
    url(r'^ajax/followers/(?P<username>[\w.@+-]+)[/]+$', views.ajaxfollowers, name='ajaxfollowers'),
    url(r'^ajax/following/(?P<username>[\w.@+-]+)[/]+$', views.ajaxfollowing, name='ajaxfollowing'),
    url(r'^ajax/activities[/]+$', views.ajaxactivities, name='ajaxactivities'),
    url(r'^ajax/likeactivity/(?P<activity>\d+)[/]+$', views.ajaxlikeactivity, name='ajaxlikeactivity'),
    url(r'^ajax/feed[/]+$', views.ajaxfeed, name='ajaxfeed'),
    url(r'^ajax/eventranking/(?P<event>\d+)/(?P<language>\w+)[/]+$', views.ajaxeventranking, name='ajaxeventranking'),
    url(r'^ajax/staff/verification/(?P<verification>\d+)/inprogress[/]+$', views.ajaxverification, {'status': 2}, name='ajaxverification'),
    url(r'^ajax/staff/verification/(?P<verification>\d+)/cancel[/]+$', views.ajaxverification, {'status': 1}, name='ajaxverification'),
    url(r'^ajax/staff/verification/(?P<verification>\d+)/deleteimage/(?P<image>\d+)[/]+$', views.ajaxstaffverificationdeleteimage, name='ajaxstaffverificationdeleteimage'),
    url(r'^ajax/staff/report/(?P<report_id>\d+)/accept/$', views.ajaxreport, {'status': 'accept'}, name='ajaxreport'),
    url(r'^ajax/staff/report/(?P<report_id>\d+)/reject/$', views.ajaxreport, {'status': 'reject'}, name='ajaxreport'),
    url(r'^ajax/trivia/share/', views.sharetrivia, name='sharetrivia'),

    url(r'^autocomplete/user/', autocomplete_views.UserAutocomplete.as_view(), name='autocomplete-user'),

    url(r'^avatar/twitter/(?P<username>[\w.@+-]+)[/]+$', views.avatar_twitter, name='avatar_twitter'),
    url(r'^avatar/facebook/(?P<username>[\w.@+-]+)[/]+$', views.avatar_facebook, name='avatar_facebook'),

    url(r'^i18n/', include('django.conf.urls.i18n')),
)
<commit_msg>Handle events with 2 SRs + events in EN that merge 2 events + page to see future events<commit_after># -*- coding: utf-8 -*-
from django.conf.urls import include, patterns, url
from django.conf import settings
from web import views
from web import autocomplete_views

urlpatterns = patterns('',
    url(r'^$', views.index, name='index'),
    url(r'^links[/]+$', views.links, name='links'),

    url(r'^cards[/]+$', views.cards, name='cards'),
    url(r'^card[s]?/(?P<card>\d+)[/]$', views.cards, name='cards'),
    url(r'^cards[s]?/(?P<card>\d+)/[\w-]+[/]+$', views.cards, name='cards'),

    url(r'^songs[/]+$', views.songs, name='songs'),
    url(r'^song[s]?/(?P<song>[^/]+)[/]$', views.songs, name='songs'),

    url(r'^create[/]+$', views.create, name='create'),
    url(r'^edit[/]+$', views.edit, name='edit'),
    url(r'^login[/]+$', views.login_custom_view, name='login'),
    url(r'^setaccountonlogin[/]+$', views.setaccountonlogin, name='setaccountonlogin'),
    url(r'^logout[/]+$', 'django.contrib.auth.views.logout', {'next_page': '/'}),
    url(r'^addaccount[/]+$', views.addaccount, name='addaccount'),
    url(r'^editaccount/(?P<account>\d+)[/]+$', views.editaccount, name='editaccount'),
    url(r'^reportaccount/(?P<account>\d+)[/]+$', views.report, name='reportaccount'),
    url(r'^reporteventparticipation/(?P<eventparticipation>\d+)[/]+$', views.report, name='reporteventparticipation'),
    url(r'^reportuser/(?P<user>[\w.@+-]+)[/]+$', views.report, name='reportuser'),
    url(r'^reportactivity/(?P<activity>\d+)[/]+$', views.report, name='reportactivity'),

    url(r'^addteam/(?P<account>\d+)[/]+$', views.addteam, name='addteam'),
    url(r'^editteam/(?P<team>\d+)[/]+$', views.editteam, name='editteam'),
    url(r'^user[s]?/(?P<username>[\w.@+-]+)[/]+$', views.profile, name='profile'),
    url(r'^users[/]+$', views.users, name='users'),
    url(r'^events[/]+$', views.events, name='events'),
    url(r'^event[s]?/(?P<event>[^/]+)[/]+$', views.event, name='event'),
    url(r'^event[s]?/(?P<event>[^/]+)/participations[/]+$', views.eventparticipations, name='eventparticipations'),
    url(r'^idols[/]+$', views.idols, name='idols'),
    url(r'^idol[s]?/(?P<idol>[^/]+)[/]+$', views.idol, name='idol'),
    url(r'^collections[/]+$', views.collections, name='collections'),
    url(r'^collection[s]?/(?P<collection>[^/]+)[/]+$', views.collection, name='collection'),
    url(r'^discussions[/]+$', views.discussions, name='discussions'),
    url(r'^discussion[s]?/(?P<discussion>[\w_]+)[/]$', views.discussion, name='discussion'),
    url(r'^activities/(?P<activity>\d+)[/]+$', views.activity, name='activity'),
    url(r'^map[/]+$', views.mapview, name='map'),
    url(r'^trivia[/]+$', views.trivia, name='trivia'),
    url(r'^about[/]+$', views.aboutview, name='about'),
    url(r'^android[/]+$', views.android, name='android'),
    url(r'^cards/albumbuilder[/]+$', views.albumbuilder, name='albumbuilder'),
    url(r'^cards/initialsetup[/]+$', views.initialsetup, name='initialsetup'),
    url(r'^skillup[/]+$', views.skillup, name='skillup'),
    url(r'^staff/verifications[/]$', views.staff_verifications, name='staff_verifications'),
    url(r'^staff/verification/(?P<verification>\d+)[/]$', views.staff_verification, name='staff_verification'),
    url(r'^staff/reports[/]$', views.staff_reports, name='staff_reports'),
    url(r'^staff/moderation[/]$', views.staff_moderation, name='staff_moderation'),
    url(r'^staff/database[/]$', views.staff_database, name='staff_database'),
    url(r'^staff/database/(?P<script>[\w -_]+)[/]$', views.staff_database_script, name='staff_database_script'),
    url(r'^urpairs[/]+$', views.urpairs, name='urpairs'),
    url(r'^english_future[/]+$', views.english_future, name='english_future'),
    url(r'^.sicaltriofestival[/]+$', views.usicaltriofestival, name='usicaltriofestival'),

    url(r'^password_reset[/]+$', 'django.contrib.auth.views.password_reset', {
        'template_name': 'password/password_reset_form.html',
        'html_email_template_name': 'password/password_reset_email_html.html',
        'from_email': settings.AWS_PASSWORD_EMAIL,
     }, name='password_reset'),
    url(r'^password_reset/done[/]+$', 'django.contrib.auth.views.password_reset_done', {
        'template_name': 'password/password_reset_done.html'
    }, name='password_reset_done'),
    url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', views.password_reset_confirm, {
        'template_name': 'password/password_reset_confirm.html'
    }, name='password_reset_confirm'),
    url(r'^reset/done[/]+$', 'django.contrib.auth.views.password_reset_complete', {
        'template_name': 'password/password_reset_complete.html'
    }, name='password_reset_complete'),

    url(r'^ajax/modal/(?P<hash>\w+)[/]+$', views.ajaxmodal, name='ajaxmodal'),
    url(r'^ajax/addcard[/]+$', views.ajaxaddcard, name='ajaxaddcard'),
    url(r'^ajax/editcard/(?P<ownedcard>\d+)[/]+$', views.ajaxeditcard, name='ajaxeditcard'),
    url(r'^ajax/deletecard/(?P<ownedcard>\d+)[/]+$', views.ajaxdeletecard, name='ajaxdeletecard'),
    url(r'^ajax/albumbuilder/editcard/(?P<ownedcard_id>\d+)[/]+$', views.ajax_albumbuilder_editcard, name='ajax_albumbuilder_editcard'),
    url(r'^ajax/albumbuilder/addcard/(?P<card_id>\d+)[/]+$', views.ajax_albumbuilder_addcard, name='ajax_albumbuilder_addcard'),
    url(r'^ajax/deletelink/(?P<link>\d+)[/]+$', views.ajaxdeletelink, name='ajaxdeletelink'),
    url(r'^ajax/cards[/]+$', views.ajaxcards, name='ajaxcards'),
    url(r'^ajax/songs[/]+$', views.ajaxsongs, name='ajaxsongs'),
    url(r'^ajax/users[/]+$', views.ajaxusers, name='ajaxusers'),
    url(r'^ajax/accounttab/(?P<account>\d+)/(?P<tab>\w+)[/]+$', views.ajaxaccounttab, name='ajaxaccounttab'),
    url(r'^ajax/accounttab/(?P<account>\d+)/(?P<tab>\w+)/more[/]+$', views.ajaxaccounttab, { 'more': True }, name='ajaxaccounttab'),
    url(r'^ajax/follow/(?P<username>[\w.@+-]+)[/]+$', views.ajaxfollow, name='ajaxfollow'),
    url(r'^ajax/followers/(?P<username>[\w.@+-]+)[/]+$', views.ajaxfollowers, name='ajaxfollowers'),
    url(r'^ajax/following/(?P<username>[\w.@+-]+)[/]+$', views.ajaxfollowing, name='ajaxfollowing'),
    url(r'^ajax/activities[/]+$', views.ajaxactivities, name='ajaxactivities'),
    url(r'^ajax/likeactivity/(?P<activity>\d+)[/]+$', views.ajaxlikeactivity, name='ajaxlikeactivity'),
    url(r'^ajax/feed[/]+$', views.ajaxfeed, name='ajaxfeed'),
    url(r'^ajax/eventranking/(?P<event>\d+)/(?P<language>\w+)[/]+$', views.ajaxeventranking, name='ajaxeventranking'),
    url(r'^ajax/staff/verification/(?P<verification>\d+)/inprogress[/]+$', views.ajaxverification, {'status': 2}, name='ajaxverification'),
    url(r'^ajax/staff/verification/(?P<verification>\d+)/cancel[/]+$', views.ajaxverification, {'status': 1}, name='ajaxverification'),
    url(r'^ajax/staff/verification/(?P<verification>\d+)/deleteimage/(?P<image>\d+)[/]+$', views.ajaxstaffverificationdeleteimage, name='ajaxstaffverificationdeleteimage'),
    url(r'^ajax/staff/report/(?P<report_id>\d+)/accept/$', views.ajaxreport, {'status': 'accept'}, name='ajaxreport'),
    url(r'^ajax/staff/report/(?P<report_id>\d+)/reject/$', views.ajaxreport, {'status': 'reject'}, name='ajaxreport'),
    url(r'^ajax/trivia/share/', views.sharetrivia, name='sharetrivia'),

    url(r'^autocomplete/user/', autocomplete_views.UserAutocomplete.as_view(), name='autocomplete-user'),

    url(r'^avatar/twitter/(?P<username>[\w.@+-]+)[/]+$', views.avatar_twitter, name='avatar_twitter'),
    url(r'^avatar/facebook/(?P<username>[\w.@+-]+)[/]+$', views.avatar_facebook, name='avatar_facebook'),

    url(r'^i18n/', include('django.conf.urls.i18n')),
)
<|endoftext|>


<commit_before>from PyQt4.QtGui import (QDockWidget, QListWidgetItem, QMenu, QAction,
                         QMessageBox)
from PyQt4.QtCore import Qt, pyqtSignal
from spyderlib.widgets.dicteditor import DictEditor
from spyderlib.utils.qthelpers import get_icon
import neo

from spykeutils.plugin.data_provider_neo import NeoDataProvider
import spykeutils.tools

from neo_navigation_ui import Ui_neoNavigationDock
from .. import api

class NeoNavigationDock(QDockWidget, Ui_neoNavigationDock):
    """ Implements a navigation dock for Neo hierarchies. Tightly coupled
    with :class:`main_window_neo.MainWindowNeo`, the main reason for this
    class to exist is to keep the dock out of the general ui file.
    """

    object_removed = pyqtSignal()  # Signal to remove an object

    def __init__(self, parent):
        QDockWidget.__init__(self, parent)
        self.parent = parent

        self.setupUi(self)

        self.neoBlockList.itemDoubleClicked.connect(
            self._edit_item_annotations)
        self.neoSegmentList.itemDoubleClicked.connect(
            self._edit_item_annotations)
        self.neoChannelGroupList.itemDoubleClicked.connect(
            self._edit_item_annotations)
        self.neoChannelList.itemDoubleClicked.connect(
            self._edit_item_annotations)
        self.neoUnitList.itemDoubleClicked.connect(
            self._edit_item_annotations)

    def clear(self):
        """ Clear all lists
        """
        self.neoBlockList.clear()

    def get_letter_id(self, id_, small=False):
        """ Return a name consisting of letters given an integer
        """
        if id_ < 0:
            return ''

        name = ''
        id_ += 1
        if small:
            start = ord('a') - 1
        else:
            start = ord('A') - 1
        while id_ >= 1:
            name += str(chr(start + (id_ % 26)))
            id_ /= 26
        return name[::-1]

    def populate_neo_block_list(self):
        """ Fill the block list with appropriate entries.
        Qt.UserRole: The :class:`neo.Block` object
        """
        self.neoBlockList.clear()

        filters = self.parent.get_active_filters('Block')

        blocks = self.parent.filter_list(
            self.parent.block_names.keys(), filters)
        for b in blocks:
            if self.parent.is_filtered(b, filters):
                continue

            item = QListWidgetItem(self.parent.block_names[b])
            item.setData(Qt.UserRole, b)
            self.neoBlockList.addItem(item)

        self.neoBlockList.setCurrentRow(0)

    def populate_neo_segment_list(self):
        """ Fill the segment list with appropriate entries.
        Qt.UserRole: The :class:`neo.Segment` object
        """
        self.neoSegmentList.clear()

        filters = self.parent.get_active_filters('Segment')

        for item in self.neoBlockList.selectedItems():
            block = item.data(Qt.UserRole)

            segments = self.parent.filter_list(block.segments, filters)
            for i, s in enumerate(segments):
                if self.parent.is_filtered(s, filters):
                    continue

                if s.name:
                    name = s.name + ' (%s-%i)' % \
                        (self.parent.block_ids[s.block], i)
                else:
                    name = '%s-%i' % (self.parent.block_ids[s.block], i)

                new_item = QListWidgetItem(name)
                new_item.setData(Qt.UserRole, s)
                self.neoSegmentList.addItem(new_item)

        self.neoSegmentList.setCurrentRow(0)

    def populate_neo_channel_group_list(self):
        """ Fill the channel group list with appropriate entries.
        Qt.UserRole: The :class:`neo.RecordingChannelGroup` object
        """
        self.neoChannelGroupList.clear()
        self.parent.channel_group_names.clear()

        filters = self.parent.get_active_filters(
            'Recording Channel Group')

        for item in self.neoBlockList.selectedItems():
            block = item.data(Qt.UserRole)

            rcgs = self.parent.filter_list(
                block.recordingchannelgroups, filters)
            for i, rcg in enumerate(rcgs):
                if self.parent.is_filtered(rcg, filters):
                    continue

                self.parent.channel_group_names[rcg] = '%s-%s' % (
                    self.parent.block_ids[rcg.block],
                    self.get_letter_id(i, True))
                if rcg.name:
                    name = rcg.name + ' (%s)' % \
                                      self.parent.channel_group_names[rcg]
                else:
                    name = self.parent.channel_group_names[rcg]
                new_item = QListWidgetItem(name)
                new_item.setData(Qt.UserRole, rcg)
                self.neoChannelGroupList.addItem(new_item)

        self.neoChannelGroupList.setCurrentRow(0)

    def populate_neo_channel_list(self):
        """ Fill the channel list with appropriate entries. Data slots:
        Qt.UserRole: The :class:`neo.RecordingChannel`
        Qt.UserRole+1: The channel index
        """
        self.neoChannelList.clear()
        channels = set()

        filters = self.parent.get_active_filters(
            'Recording Channel')

        for item in self.neoChannelGroupList.selectedItems():
            channel_group = item.data(Qt.UserRole)

            rcs = self.parent.filter_list(
                channel_group.recordingchannels, filters)
            for rc in rcs:
                if not api.config['duplicate_channels'] and rc in channels:
                    continue
                if self.parent.is_filtered(rc, filters):
                    continue

                channels.add(rc)
                identifier = '%s.%d' % \
                             (self.parent.channel_group_names[channel_group],
                              rc.index)
                if rc.name:
                    name = rc.name + ' (%s)' % identifier
                else:
                    name = identifier
                new_item = QListWidgetItem(name)
                new_item.setData(Qt.UserRole, rc)
                new_item.setData(Qt.UserRole + 1, rc.index)
                self.neoChannelList.addItem(new_item)
                self.neoChannelList.setItemSelected(new_item, True)

    def populate_neo_unit_list(self):
        """ Fill the unit list with appropriate entries.
        Qt.UserRole: The :class:`neo.Unit` object
        """
        self.neoUnitList.clear()

        filters = self.parent.get_active_filters('Unit')

        for item in self.neoChannelGroupList.selectedItems():
            rcg = item.data(Qt.UserRole)

            units = self.parent.filter_list(rcg.units, filters)
            for i, u in enumerate(units):
                if self.parent.is_filtered(u, filters):
                    continue
                if u.name:
                    name = u.name + ' (%s-%d)' % \
                           (self.parent.channel_group_names[rcg], i)
                else:
                    name = '%s-%d' % (self.parent.channel_group_names[rcg], i)
                new_item = QListWidgetItem(name)
                new_item.setData(Qt.UserRole, u)
                self.neoUnitList.addItem(new_item)

    def on_neoBlockList_itemSelectionChanged(self):
        self.populate_neo_channel_group_list()
        self.populate_neo_segment_list()

    def on_neoChannelGroupList_itemSelectionChanged(self):
        self.populate_neo_channel_list()
        self.populate_neo_unit_list()

    def _edit_item_annotations(self, item):
        self.edit_annotations(item.data(Qt.UserRole))

    def edit_annotations(self, data):
        """ Edit annotations of a Neo object.
        """
        editor = DictEditor(self.parent)
        title = 'Edit annotations'
        if data.name:
            title += ' for %s' % data.name
        editor.setup(data.annotations, title)
        editor.accepted.connect(
            lambda: self._editor_ok(data, editor))
        editor.show()
        editor.raise_()
        editor.activateWindow()

    def _editor_ok(self, data, editor):
        data.annotations = editor.get_value()

    def remove_selected(self, list_widget):
        """ Remove all selected objects from the given list widget.
        """
        items = list_widget.selectedItems()
        if len(items) < 1:
            return

        question = ('Do you really want to remove %d %s' %
                    (len(items),
                    type(items[0].data(Qt.UserRole)).__name__))
        if len(items) > 1:
            question += 's'
        question += '?'

        if QMessageBox.question(
                self, 'Please confirm', question,
                QMessageBox.Yes | QMessageBox.No) == QMessageBox.No:
            return

        for i in list_widget.selectedItems():
            data = i.data(Qt.UserRole)
            if isinstance(data, neo.Block):
                self.parent.block_names.pop(data)
            else:
                spykeutils.tools.remove_from_hierarchy(data)
            list_widget.setItemSelected(i, False)

        self.object_removed.emit()

    def _context_actions(self, list_widget):
        c = list_widget.currentItem()
        if not c:
            return []

        data = c.data(Qt.UserRole)

        edit_action = QAction(get_icon('edit.png'),
                              'Edit annotations...', self)
        edit_action.triggered.connect(
            lambda x:
            self._edit_item_annotations(c))

        delete_name = 'Delete %s' % type(data).__name__
        if len(list_widget.selectedItems()) > 1:
            delete_name += 's'
        delete_action = QAction(get_icon('editdelete.png'),
                                delete_name, self)
        delete_action.triggered.connect(
            lambda x:
            self.remove_selected(list_widget))

        return [edit_action, delete_action]

    def on_neoBlockList_customContextMenuRequested(self, pos):
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoBlockList))
        context_menu.popup(self.neoBlockList.mapToGlobal(pos))

    def on_neoSegmentList_customContextMenuRequested(self, pos):
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoSegmentList))
        context_menu.popup(self.neoSegmentList.mapToGlobal(pos))

    def on_neoChannelGroupList_customContextMenuRequested(self, pos):
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoChannelGroupList))
        context_menu.popup(self.neoChannelGroupList.mapToGlobal(pos))

    def on_neoChannelList_customContextMenuRequested(self, pos):
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoChannelList))
        context_menu.popup(self.neoChannelList.mapToGlobal(pos))

    def on_neoUnitList_customContextMenuRequested(self, pos):
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoUnitList))
        context_menu.popup(self.neoUnitList.mapToGlobal(pos))

    def blocks(self):
        """ Return selected :class:`neo.Block` objects.
        """
        return [t.data(Qt.UserRole) for t in
                self.neoBlockList.selectedItems()]

    def segments(self):
        """ Return selected :class:`neo.Segment` objects.
        """
        return [t.data(Qt.UserRole) for t in
                self.neoSegmentList.selectedItems()]

    def recording_channel_groups(self):
        """ Return selected :class:`neo.RecordingChannelGroup` objects.
        """
        return [t.data(Qt.UserRole) for t in
                self.neoChannelGroupList.selectedItems()]

    def recording_channels(self):
        """ Return selected :class:`neo.RecordingChannel` objects.
        """
        return [t.data(Qt.UserRole) for t in
                self.neoChannelList.selectedItems()]

    def units(self):
        """ Return selected :class:`neo.Unit` objects.
        """
        return [t.data(Qt.UserRole) for t in
                self.neoUnitList.selectedItems()]

    def set_selection(self, data):
        """ Set the selected data. All data in the selection has to be loaded
        before!
        """
        block_list = []
        for b in data['blocks']:
            loaded = NeoDataProvider.get_block(b[1], b[0], False)
            if loaded is None:
                raise IOError('One of the files contained in the '
                              'selection could not be loaded!')
            block_list.append(loaded)
        rcg_list = [block_list[rcg[1]].recordingchannelgroups[rcg[0]]
                    for rcg in data['channel_groups']]

        # Select blocks
        for i in self.neoBlockList.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            block = i.data(Qt.UserRole)
            t = [NeoDataProvider.block_indices[block],
                 self.parent.block_files[block]]
            i.setSelected(t in data['blocks'])

        # Select segments
        for i in self.neoSegmentList.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            segment = i.data(Qt.UserRole)
            if not segment.block in block_list:
                i.setSelected(False)
                continue

            seg_idx = segment.block.segments.index(segment)
            block_idx = block_list.index(segment.block)
            i.setSelected([seg_idx, block_idx] in data['segments'])

        # Select recording channel groups
        for i in self.neoChannelGroupList.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            rcg = i.data(Qt.UserRole)
            if not rcg.block in block_list:
                i.setSelected(False)
                continue

            rcg_idx = rcg.block.recordingchannelgroups.index(rcg)
            block_idx = block_list.index(rcg.block)
            i.setSelected([rcg_idx, block_idx] in data['channel_groups'])

        # Select channels
        rcg_set = set(rcg_list)
        for i in self.neoChannelList.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            i.setSelected(False)
            channel = i.data(Qt.UserRole)
            if not set(channel.recordingchannelgroups).intersection(rcg_set):
                continue

            for rcg in channel.recordingchannelgroups:
                if [rcg.recordingchannels.index(channel),
                        rcg_list.index(rcg)] in data['channels']:
                    i.setSelected(True)
                    break

        # Select units
        for i in self.neoUnitList.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            unit = i.data(Qt.UserRole)
            if unit.recordingchannelgroup not in rcg_list:
                i.setSelected(False)
                continue

            rcg_idx = rcg_list.index(unit.recordingchannelgroup)
            unit_idx = unit.recordingchannelgroup.units.index(unit)
            i.setSelected([unit_idx, rcg_idx] in data['units'])<commit_msg>Fixed: letter ids >= 26<commit_after>from PyQt4.QtGui import (QDockWidget, QListWidgetItem, QMenu, QAction,
                         QMessageBox)
from PyQt4.QtCore import Qt, pyqtSignal
from spyderlib.widgets.dicteditor import DictEditor
from spyderlib.utils.qthelpers import get_icon
import neo

from spykeutils.plugin.data_provider_neo import NeoDataProvider
import spykeutils.tools

from neo_navigation_ui import Ui_neoNavigationDock
from .. import api

class NeoNavigationDock(QDockWidget, Ui_neoNavigationDock):
    """ Implements a navigation dock for Neo hierarchies. Tightly coupled
    with :class:`main_window_neo.MainWindowNeo`, the main reason for this
    class to exist is to keep the dock out of the general ui file.
    """

    object_removed = pyqtSignal()  # Signal to remove an object

    def __init__(self, parent):
        QDockWidget.__init__(self, parent)
        self.parent = parent

        self.setupUi(self)

        self.neoBlockList.itemDoubleClicked.connect(
            self._edit_item_annotations)
        self.neoSegmentList.itemDoubleClicked.connect(
            self._edit_item_annotations)
        self.neoChannelGroupList.itemDoubleClicked.connect(
            self._edit_item_annotations)
        self.neoChannelList.itemDoubleClicked.connect(
            self._edit_item_annotations)
        self.neoUnitList.itemDoubleClicked.connect(
            self._edit_item_annotations)

    def clear(self):
        """ Clear all lists
        """
        self.neoBlockList.clear()

    def get_letter_id(self, id_, small=False):
        """ Return a name consisting of letters given an integer
        """
        if id_ < 0:
            return ''

        name = ''
        id_ += 1
        if small:
            start = ord('a') - 1
        else:
            start = ord('A') - 1
        while id_ >= 1:
            name += str(chr(start + (id_ % 27)))
            id_ /= 27
        return name[::-1]

    def populate_neo_block_list(self):
        """ Fill the block list with appropriate entries.
        Qt.UserRole: The :class:`neo.Block` object
        """
        self.neoBlockList.clear()

        filters = self.parent.get_active_filters('Block')

        blocks = self.parent.filter_list(
            self.parent.block_names.keys(), filters)
        for b in blocks:
            if self.parent.is_filtered(b, filters):
                continue

            item = QListWidgetItem(self.parent.block_names[b])
            item.setData(Qt.UserRole, b)
            self.neoBlockList.addItem(item)

        self.neoBlockList.setCurrentRow(0)

    def populate_neo_segment_list(self):
        """ Fill the segment list with appropriate entries.
        Qt.UserRole: The :class:`neo.Segment` object
        """
        self.neoSegmentList.clear()

        filters = self.parent.get_active_filters('Segment')

        for item in self.neoBlockList.selectedItems():
            block = item.data(Qt.UserRole)

            segments = self.parent.filter_list(block.segments, filters)
            for i, s in enumerate(segments):
                if self.parent.is_filtered(s, filters):
                    continue

                if s.name:
                    name = s.name + ' (%s-%i)' % \
                        (self.parent.block_ids[s.block], i)
                else:
                    name = '%s-%i' % (self.parent.block_ids[s.block], i)

                new_item = QListWidgetItem(name)
                new_item.setData(Qt.UserRole, s)
                self.neoSegmentList.addItem(new_item)

        self.neoSegmentList.setCurrentRow(0)

    def populate_neo_channel_group_list(self):
        """ Fill the channel group list with appropriate entries.
        Qt.UserRole: The :class:`neo.RecordingChannelGroup` object
        """
        self.neoChannelGroupList.clear()
        self.parent.channel_group_names.clear()

        filters = self.parent.get_active_filters(
            'Recording Channel Group')

        for item in self.neoBlockList.selectedItems():
            block = item.data(Qt.UserRole)

            rcgs = self.parent.filter_list(
                block.recordingchannelgroups, filters)
            for i, rcg in enumerate(rcgs):
                if self.parent.is_filtered(rcg, filters):
                    continue

                self.parent.channel_group_names[rcg] = '%s-%s' % (
                    self.parent.block_ids[rcg.block],
                    self.get_letter_id(i, True))
                if rcg.name:
                    name = rcg.name + ' (%s)' % \
                                      self.parent.channel_group_names[rcg]
                else:
                    name = self.parent.channel_group_names[rcg]
                new_item = QListWidgetItem(name)
                new_item.setData(Qt.UserRole, rcg)
                self.neoChannelGroupList.addItem(new_item)

        self.neoChannelGroupList.setCurrentRow(0)

    def populate_neo_channel_list(self):
        """ Fill the channel list with appropriate entries. Data slots:
        Qt.UserRole: The :class:`neo.RecordingChannel`
        Qt.UserRole+1: The channel index
        """
        self.neoChannelList.clear()
        channels = set()

        filters = self.parent.get_active_filters(
            'Recording Channel')

        for item in self.neoChannelGroupList.selectedItems():
            channel_group = item.data(Qt.UserRole)

            rcs = self.parent.filter_list(
                channel_group.recordingchannels, filters)
            for rc in rcs:
                if not api.config['duplicate_channels'] and rc in channels:
                    continue
                if self.parent.is_filtered(rc, filters):
                    continue

                channels.add(rc)
                identifier = '%s.%d' % \
                             (self.parent.channel_group_names[channel_group],
                              rc.index)
                if rc.name:
                    name = rc.name + ' (%s)' % identifier
                else:
                    name = identifier
                new_item = QListWidgetItem(name)
                new_item.setData(Qt.UserRole, rc)
                new_item.setData(Qt.UserRole + 1, rc.index)
                self.neoChannelList.addItem(new_item)
                self.neoChannelList.setItemSelected(new_item, True)

    def populate_neo_unit_list(self):
        """ Fill the unit list with appropriate entries.
        Qt.UserRole: The :class:`neo.Unit` object
        """
        self.neoUnitList.clear()

        filters = self.parent.get_active_filters('Unit')

        for item in self.neoChannelGroupList.selectedItems():
            rcg = item.data(Qt.UserRole)

            units = self.parent.filter_list(rcg.units, filters)
            for i, u in enumerate(units):
                if self.parent.is_filtered(u, filters):
                    continue
                if u.name:
                    name = u.name + ' (%s-%d)' % \
                           (self.parent.channel_group_names[rcg], i)
                else:
                    name = '%s-%d' % (self.parent.channel_group_names[rcg], i)
                new_item = QListWidgetItem(name)
                new_item.setData(Qt.UserRole, u)
                self.neoUnitList.addItem(new_item)

    def on_neoBlockList_itemSelectionChanged(self):
        self.populate_neo_channel_group_list()
        self.populate_neo_segment_list()

    def on_neoChannelGroupList_itemSelectionChanged(self):
        self.populate_neo_channel_list()
        self.populate_neo_unit_list()

    def _edit_item_annotations(self, item):
        self.edit_annotations(item.data(Qt.UserRole))

    def edit_annotations(self, data):
        """ Edit annotations of a Neo object.
        """
        editor = DictEditor(self.parent)
        title = 'Edit annotations'
        if data.name:
            title += ' for %s' % data.name
        editor.setup(data.annotations, title)
        editor.accepted.connect(
            lambda: self._editor_ok(data, editor))
        editor.show()
        editor.raise_()
        editor.activateWindow()

    def _editor_ok(self, data, editor):
        data.annotations = editor.get_value()

    def remove_selected(self, list_widget):
        """ Remove all selected objects from the given list widget.
        """
        items = list_widget.selectedItems()
        if len(items) < 1:
            return

        question = ('Do you really want to remove %d %s' %
                    (len(items),
                    type(items[0].data(Qt.UserRole)).__name__))
        if len(items) > 1:
            question += 's'
        question += '?'

        if QMessageBox.question(
                self, 'Please confirm', question,
                QMessageBox.Yes | QMessageBox.No) == QMessageBox.No:
            return

        for i in list_widget.selectedItems():
            data = i.data(Qt.UserRole)
            if isinstance(data, neo.Block):
                self.parent.block_names.pop(data)
            else:
                spykeutils.tools.remove_from_hierarchy(data)
            list_widget.setItemSelected(i, False)

        self.object_removed.emit()

    def _context_actions(self, list_widget):
        c = list_widget.currentItem()
        if not c:
            return []

        data = c.data(Qt.UserRole)

        edit_action = QAction(get_icon('edit.png'),
                              'Edit annotations...', self)
        edit_action.triggered.connect(
            lambda x:
            self._edit_item_annotations(c))

        delete_name = 'Delete %s' % type(data).__name__
        if len(list_widget.selectedItems()) > 1:
            delete_name += 's'
        delete_action = QAction(get_icon('editdelete.png'),
                                delete_name, self)
        delete_action.triggered.connect(
            lambda x:
            self.remove_selected(list_widget))

        return [edit_action, delete_action]

    def on_neoBlockList_customContextMenuRequested(self, pos):
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoBlockList))
        context_menu.popup(self.neoBlockList.mapToGlobal(pos))

    def on_neoSegmentList_customContextMenuRequested(self, pos):
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoSegmentList))
        context_menu.popup(self.neoSegmentList.mapToGlobal(pos))

    def on_neoChannelGroupList_customContextMenuRequested(self, pos):
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoChannelGroupList))
        context_menu.popup(self.neoChannelGroupList.mapToGlobal(pos))

    def on_neoChannelList_customContextMenuRequested(self, pos):
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoChannelList))
        context_menu.popup(self.neoChannelList.mapToGlobal(pos))

    def on_neoUnitList_customContextMenuRequested(self, pos):
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoUnitList))
        context_menu.popup(self.neoUnitList.mapToGlobal(pos))

    def blocks(self):
        """ Return selected :class:`neo.Block` objects.
        """
        return [t.data(Qt.UserRole) for t in
                self.neoBlockList.selectedItems()]

    def segments(self):
        """ Return selected :class:`neo.Segment` objects.
        """
        return [t.data(Qt.UserRole) for t in
                self.neoSegmentList.selectedItems()]

    def recording_channel_groups(self):
        """ Return selected :class:`neo.RecordingChannelGroup` objects.
        """
        return [t.data(Qt.UserRole) for t in
                self.neoChannelGroupList.selectedItems()]

    def recording_channels(self):
        """ Return selected :class:`neo.RecordingChannel` objects.
        """
        return [t.data(Qt.UserRole) for t in
                self.neoChannelList.selectedItems()]

    def units(self):
        """ Return selected :class:`neo.Unit` objects.
        """
        return [t.data(Qt.UserRole) for t in
                self.neoUnitList.selectedItems()]

    def set_selection(self, data):
        """ Set the selected data. All data in the selection has to be loaded
        before!
        """
        block_list = []
        for b in data['blocks']:
            loaded = NeoDataProvider.get_block(b[1], b[0], False)
            if loaded is None:
                raise IOError('One of the files contained in the '
                              'selection could not be loaded!')
            block_list.append(loaded)
        rcg_list = [block_list[rcg[1]].recordingchannelgroups[rcg[0]]
                    for rcg in data['channel_groups']]

        # Select blocks
        for i in self.neoBlockList.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            block = i.data(Qt.UserRole)
            t = [NeoDataProvider.block_indices[block],
                 self.parent.block_files[block]]
            i.setSelected(t in data['blocks'])

        # Select segments
        for i in self.neoSegmentList.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            segment = i.data(Qt.UserRole)
            if not segment.block in block_list:
                i.setSelected(False)
                continue

            seg_idx = segment.block.segments.index(segment)
            block_idx = block_list.index(segment.block)
            i.setSelected([seg_idx, block_idx] in data['segments'])

        # Select recording channel groups
        for i in self.neoChannelGroupList.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            rcg = i.data(Qt.UserRole)
            if not rcg.block in block_list:
                i.setSelected(False)
                continue

            rcg_idx = rcg.block.recordingchannelgroups.index(rcg)
            block_idx = block_list.index(rcg.block)
            i.setSelected([rcg_idx, block_idx] in data['channel_groups'])

        # Select channels
        rcg_set = set(rcg_list)
        for i in self.neoChannelList.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            i.setSelected(False)
            channel = i.data(Qt.UserRole)
            if not set(channel.recordingchannelgroups).intersection(rcg_set):
                continue

            for rcg in channel.recordingchannelgroups:
                if [rcg.recordingchannels.index(channel),
                        rcg_list.index(rcg)] in data['channels']:
                    i.setSelected(True)
                    break

        # Select units
        for i in self.neoUnitList.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            unit = i.data(Qt.UserRole)
            if unit.recordingchannelgroup not in rcg_list:
                i.setSelected(False)
                continue

            rcg_idx = rcg_list.index(unit.recordingchannelgroup)
            unit_idx = unit.recordingchannelgroup.units.index(unit)
            i.setSelected([unit_idx, rcg_idx] in data['units'])<|endoftext|>