<commit_before>import urllib.request, urllib.error, urllib.parse
import json
from random import choice
from math import fabs
from os import path
import pickle
import re

import plugin

class Plugin(plugin.Plugin):
    """ Fresh MyAnimeList facts, milled from mal-api.com. """
    def register_commands(self):
        self.commands = [
                ('mal search <<query>>', self.search),
                ('mal set <user>', self.define),
                ('mal <user>', self.summarise),
                ('mal', self.summarise_self),
                ('mal fight <user1> <user2>', self.fight),
                ('mal fight <user>', self.fight_self),
                ('mal compare <user1> <user2>', self.compare),
                ('mal compare <user>', self.compare_self),
            ]

    def prepare(self):
        self.url = 'http://mal-api.com/%s?%s'
        self.default_args = ['format=json']
        self.user_file_path = path.expanduser(self.conf.conf['content_dir']+'mal')
        self.malusers = {}
        self.help_setup = "link a MyAnimeList account to your IRC nick with '"+self.conf.conf['comchar']+"mal set <account name>'"
        self.help_missing = 'no such MAL user \x02%s\x02'

        try:
            userfile = open(self.user_file_path, 'rb')
            self.malusers = pickle.load(userfile)
            userfile.close()
        except:
            print(' :: error reading MAL user pickle, will create one when necessary')

    def define(self, message, args):
        """ Many of these functions benefit from knowing whose IRC nicks correspond to which MAL user, so be sure to tell <pyfoot> who you are.
        $<comchar>mal set colons
        >\x02nivi\x02 is now MAL user \x02colons\x02\x03# |\x03 http://myanimelist.net/profile/\x02colons"""
        user = args['user']

        try:
            data = self.query('animelist/%s' % user)
        except urllib.error.HTTPError:
            self.irc.privmsg(message.source, self.help_missing % user, pretty=True)
        else:
            try: # just in case other instances of pyfoot have altered the file since we last read it
                userfile = open(self.user_file_path, 'rb')
            except IOError:
                print(' :: error reading MAL user pickle, creating one now')
                malusers = {}
            else:
                malusers = pickle.load(userfile)
                userfile.close()

            malusers[self.conf.alias+' '+message.nick.lower()] = user

            userfile = open(self.user_file_path, 'wb')
            pickle.dump(malusers, userfile)
            userfile.close()
            self.irc.privmsg(message.source, '\x02%s\x02 is now MAL user \x02%s\x02 | http://myanimelist.net/profile/\x02%s' % (message.nick, user, user), pretty=True)

    def maluser(self, user):
        """ Takes a user - irc or mal - and determines the appropriate MAL username """
        try:
            userfile = open(self.user_file_path, 'rb')
        except IOError:
            print(' :: no MAL user pickle found, ignoring')
            malusers = {}
        else:
            malusers = pickle.load(userfile)
            userfile.close()

        print(malusers)

        try:
            maluser = malusers[self.conf.alias+' '+user.lower()]
        except KeyError:
            return user
        else:
            return maluser

    def select(self, things, limit=5):
        # selection = sorted(animelist, key=lambda anime: anime['score'], reverse=True)[:limit]
        if len(things) <= limit:
            selection = things
        else:
            selection = []

            while len(selection) < limit:
                thing = choice(things)
                if thing not in selection:
                    selection.append(thing)

        return selection

    def oiz(self, thing):
        """ Pipe user ratings through this. """
        if thing == 0:
            return '-'
        else:
            return thing

    def summarise_self(self, message, args):
        args = {'user': message.nick}
        self.summarise(message, args)

    def summarise(self, message, args):
        """ Summarise a particular user (or yourself).
        $<comchar>mal theshillito
        >http://myanimelist.net/animelist/\x02theshillito\x02\x03# |\x03 \x0262.17\x02 days across \x02281\x02 keions\x03# |\x03 K-On!: Ura-On!\x03# :\x03 \x027\x02/\x027\x02\x03# :\x03 \x027\x02\x03# |\x03 Ah! My Goddess\x03# :\x03 \x0224\x02/\x0224\x02\x03# :\x03 \x0210\x02\x03# |\x03 Kamen no Maid Guy\x03# :\x03 \x0212\x02/\x0212\x02\x03# :\x03 \x029\x02\x03# |\x03 Makai Senki Disgaea\x03# :\x03 \x0212\x02/\x0212\x02\x03# :\x03 \x027\x02\x03# |\x03 K-On!\x03# :\x03 \x0213\x02/\x0213\x02\x03# :\x03 \x029\x02"""
        user = self.maluser(args['user'])

        try:
            data = self.query('animelist/%s' % user)
        except urllib.error.HTTPError:
            self.irc.privmsg(message.source, self.help_missing % user, pretty=True)
            return

        days = data['statistics']['days']
        animelist = data['anime']

        consumed = []
        planned = 0

        for a in animelist:
            if a['watched_status'] == 'plan to watch':
                planned += 1
            else:
                consumed.append(a)

        selection = self.select(consumed)

        if len(selection) == 0:
            showlist_string = ''
        else:
            showlist_string = ' | %s' % ' | '.join(['%s : \x02%s\x02/\x02%s\x02 : \x02%s\x02' % (a['title'], self.oiz(a['watched_episodes']),
                    self.oiz(a['episodes']), self.oiz(a['score'])) for a in selection])

        summary = 'http://myanimelist.net/animelist/\x02%s\x02 | \x02%s\x02 days across \x02%d\x02 keions%s' % (user, days, len(consumed), showlist_string)
        self.irc.privmsg(message.source, summary, pretty=True)

    def common_keions(self, users):
        """ Get a list of tuples of keions that any two users have in common """
        ud_list = []
        for user in users:
            try:
                data = self.query('animelist/%s' % user)
            except urllib.error.HTTPError:
                return self.help_missing % user
            ud_list.append(data)

        common = []

        for a1 in ud_list[0]['anime']:
            for a2 in ud_list[1]['anime']:
                if a1['id'] == a2['id'] and a1['watched_status'] != 'plan to watch' and a2['watched_status'] != 'plan to watch':
                    common.append((a1, a2))

        return common

    def compare_self(self, message, args):
        """ Like a fight, but friendlier."""
        self.compare(message, {'user1': message.nick, 'user2': args['user']})

    def compare(self, message, args):
        users = [self.maluser(u) for u in [args['user1'], args['user2']]]
        common = self.common_keions(users)
        if type(common) == str:
            self.irc.privmsg(message.source, common, pretty=True)
            return

        total_score_diff = 0
        consensus = [] # animes scored the same
        both_scored = 0

        for a1, a2 in common:
            if (a1['score'] == a2['score']) and a1['score'] != 0:
                consensus.append(a1)
            if a1['score'] != 0 and a2['score'] != 0:
                both_scored += 1

        if len(consensus) > 0:
            selection = self.select(consensus)
            self.irc.privmsg(message.source,
                    '\x02%s\x02 and \x02%s\x02 | \x02%d\x02 common keions | agreement on \x02%d\x02/\x02%d\x02 mutually scored keions | %s' % (
                    users[0], users[1], len(common), len(consensus), both_scored,
                    ' | '.join(['%s : \x02%d\x02' % (a['title'], a['score']) for a in selection])),
                    pretty=True)
        else:
            # find the closest thing to common ground we have
            smallest_gap = 10
            closest = []

            for a1, a2 in common:
                if a1['score'] != 0 and a2['score'] != 0:
                    gap = fabs(a1['score'] - a2['score'])
                    if gap < smallest_gap:
                        smallest_gap = gap
                        closest = [(a1, a2)]
                    elif gap == smallest_gap:
                        closest.append((a1, a2))

            if len(closest) > 0:
                selection = self.select(closest)
                self.irc.privmsg(message.source, "\x02%s\x02 and \x02%s\x02 | \x02%d\x02 common keions | %s" % (
                        users[0], users[1], len(common),
                        ' | '.join(['%s : \x02%d\x02, \x02%d\x02' % (a[0]['title'], a[0]['score'], a[1]['score']) for a in selection])),
                        pretty=True)
            else:
                # we have no common ground :<
                selection = self.select(common)
                self.irc.privmsg(message.source, "\x02%s\x02 and \x02%s\x02 have \x02%d\x02 keions in common | %s" % (users[0], users[1],
                        len(common), ' | '.join([a[0]['title'] for a in selection])),
                        pretty=True)

    def fight_self(self, message, args):
        """ Fight someone.
        $<comchar>mal fight xinil
        >\x02colons\x02 vs. \x02xinil\x02\x03# |\x03 average contention\x03# :\x03 \x021.12\x02\x03# |\x03 Tengen Toppa Gurren Lagann\x03# :\x03 \x024\x02 vs. \x028\x02
        If you specify an additional username, you don't have to get involved yourself.
        $<comchar>mal fight xinil theshillito
        >\x02xinil\x02 vs. \x02theshillito\x02\x03# | \x03average contention\x03# :\x03 \x022.10\x02\x03# |\x03 Cowboy Bebop\x03# :\x03 \x0210\x02 vs. \x024\x02\x03# |\x03 Fate/stay night\x03# :\x03 \x027\x02 vs. \x021\x02
        """
        self.fight(message, {
            'user1': message.nick,
            'user2': args['user']
            })

    def fight(self, message, args):
        users = [self.maluser(u) for u in [args['user1'], args['user2']]]
        common = self.common_keions(users)
        if type(common) == str:
            self.irc.privmsg(message.source, common, pretty=True)
            return

        largest_gap = 0
        contention = []
        total_gap = 0
        considered = 0

        for a1, a2 in common:
            if a1['score'] != 0 and a2['score'] != 0:
                gap = fabs(a1['score'] - a2['score'])
                total_gap += gap
                considered += 1
                if gap > largest_gap:
                    largest_gap = gap
                    contention = [(a1, a2)]
                elif gap == largest_gap:
                    contention.append((a1, a2))

        if considered > 0:
            average_gap = total_gap/float(considered)

        if len(contention) > 0:
            selection = self.select(contention)
            self.irc.privmsg(message.source, "\x02%s\x02 vs. \x02%s\x02 | average contention : \x02%.2f\x02 | %s" % (
                    users[0], users[1], average_gap,
                    ' | '. join(['%s : \x02%d\x02 vs. \x02%d\x02' % (a[0]['title'], a[0]['score'], a[1]['score']) for a in selection])),
                    pretty=True)
        else:
            self.irc.privmsg(message.source, "\x02%s\x02 and \x02%s\x02 need to watch and score more stuff" % (users[0], users[1]),
                    pretty=True)

    def search(self, message, args):
        """ $!mal search churuya
        >\x02Nyoro-n Churuya-san\x02\x03# :\x03 ONA\x03# |\x03 http://myanimelist.net/anime/5957\x03# |\x03 An anime adaptation of the 4-panel strip manga release: Nyoron Churuya-san. Based on Suzumiya Haruhi's energetic and 'always up to go' character, Tsuruya."""
        query = args['query']

        search = self.query('anime/search', ['q=%s' % urllib.parse.quote(query)])

        # data = self.query('anime/%i' % search[0]['id'])
        try:
            keion = search[0]
        except IndexError:
            self.irc.privmsg(message.source,
                    'no results | http://myanimelist.net/anime.php?q=%s' % urllib.parse.quote(query),
                    pretty=True)
            return

        for show in search:
            if show['title'].lower() == args['query'].lower():
                keion = show

        keion_page = 'http://myanimelist.net/anime/%i' % show['id']

        # the html stripping should not be here, but it is for now because fuck you
        self.irc.privmsg(message.source,
                '\x02%s\x02 : %s | %s | %s' % (keion['title'], keion['type'], keion_page, re.sub('<[^<]+?>', '', keion['synopsis'])),
                pretty=True, crop=True)

    def query(self, query, additional_args=[]):
        args = '&'.join(self.default_args+additional_args)
        print(' -- '+self.url % (query, args))
        raw_data = urllib.request.urlopen(self.url % (query, args)).read().decode('utf-8')
        data = json.loads(raw_data)
        return data

    def correlate(self, id1, id2):
        print('hi')
<commit_msg>Made docs for mal search react to comchar changes.<commit_after>import urllib.request, urllib.error, urllib.parse
import json
from random import choice
from math import fabs
from os import path
import pickle
import re

import plugin

class Plugin(plugin.Plugin):
    """ Fresh MyAnimeList facts, milled from mal-api.com. """
    def register_commands(self):
        self.commands = [
                ('mal search <<query>>', self.search),
                ('mal set <user>', self.define),
                ('mal <user>', self.summarise),
                ('mal', self.summarise_self),
                ('mal fight <user1> <user2>', self.fight),
                ('mal fight <user>', self.fight_self),
                ('mal compare <user1> <user2>', self.compare),
                ('mal compare <user>', self.compare_self),
            ]

    def prepare(self):
        self.url = 'http://mal-api.com/%s?%s'
        self.default_args = ['format=json']
        self.user_file_path = path.expanduser(self.conf.conf['content_dir']+'mal')
        self.malusers = {}
        self.help_setup = "link a MyAnimeList account to your IRC nick with '"+self.conf.conf['comchar']+"mal set <account name>'"
        self.help_missing = 'no such MAL user \x02%s\x02'

        try:
            userfile = open(self.user_file_path, 'rb')
            self.malusers = pickle.load(userfile)
            userfile.close()
        except:
            print(' :: error reading MAL user pickle, will create one when necessary')

    def define(self, message, args):
        """ Many of these functions benefit from knowing whose IRC nicks correspond to which MAL user, so be sure to tell <pyfoot> who you are.
        $<comchar>mal set colons
        >\x02nivi\x02 is now MAL user \x02colons\x02\x03# |\x03 http://myanimelist.net/profile/\x02colons"""
        user = args['user']

        try:
            data = self.query('animelist/%s' % user)
        except urllib.error.HTTPError:
            self.irc.privmsg(message.source, self.help_missing % user, pretty=True)
        else:
            try: # just in case other instances of pyfoot have altered the file since we last read it
                userfile = open(self.user_file_path, 'rb')
            except IOError:
                print(' :: error reading MAL user pickle, creating one now')
                malusers = {}
            else:
                malusers = pickle.load(userfile)
                userfile.close()

            malusers[self.conf.alias+' '+message.nick.lower()] = user

            userfile = open(self.user_file_path, 'wb')
            pickle.dump(malusers, userfile)
            userfile.close()
            self.irc.privmsg(message.source, '\x02%s\x02 is now MAL user \x02%s\x02 | http://myanimelist.net/profile/\x02%s' % (message.nick, user, user), pretty=True)

    def maluser(self, user):
        """ Takes a user - irc or mal - and determines the appropriate MAL username """
        try:
            userfile = open(self.user_file_path, 'rb')
        except IOError:
            print(' :: no MAL user pickle found, ignoring')
            malusers = {}
        else:
            malusers = pickle.load(userfile)
            userfile.close()

        print(malusers)

        try:
            maluser = malusers[self.conf.alias+' '+user.lower()]
        except KeyError:
            return user
        else:
            return maluser

    def select(self, things, limit=5):
        # selection = sorted(animelist, key=lambda anime: anime['score'], reverse=True)[:limit]
        if len(things) <= limit:
            selection = things
        else:
            selection = []

            while len(selection) < limit:
                thing = choice(things)
                if thing not in selection:
                    selection.append(thing)

        return selection

    def oiz(self, thing):
        """ Pipe user ratings through this. """
        if thing == 0:
            return '-'
        else:
            return thing

    def summarise_self(self, message, args):
        args = {'user': message.nick}
        self.summarise(message, args)

    def summarise(self, message, args):
        """ Summarise a particular user (or yourself).
        $<comchar>mal theshillito
        >http://myanimelist.net/animelist/\x02theshillito\x02\x03# |\x03 \x0262.17\x02 days across \x02281\x02 keions\x03# |\x03 K-On!: Ura-On!\x03# :\x03 \x027\x02/\x027\x02\x03# :\x03 \x027\x02\x03# |\x03 Ah! My Goddess\x03# :\x03 \x0224\x02/\x0224\x02\x03# :\x03 \x0210\x02\x03# |\x03 Kamen no Maid Guy\x03# :\x03 \x0212\x02/\x0212\x02\x03# :\x03 \x029\x02\x03# |\x03 Makai Senki Disgaea\x03# :\x03 \x0212\x02/\x0212\x02\x03# :\x03 \x027\x02\x03# |\x03 K-On!\x03# :\x03 \x0213\x02/\x0213\x02\x03# :\x03 \x029\x02"""
        user = self.maluser(args['user'])

        try:
            data = self.query('animelist/%s' % user)
        except urllib.error.HTTPError:
            self.irc.privmsg(message.source, self.help_missing % user, pretty=True)
            return

        days = data['statistics']['days']
        animelist = data['anime']

        consumed = []
        planned = 0

        for a in animelist:
            if a['watched_status'] == 'plan to watch':
                planned += 1
            else:
                consumed.append(a)

        selection = self.select(consumed)

        if len(selection) == 0:
            showlist_string = ''
        else:
            showlist_string = ' | %s' % ' | '.join(['%s : \x02%s\x02/\x02%s\x02 : \x02%s\x02' % (a['title'], self.oiz(a['watched_episodes']),
                    self.oiz(a['episodes']), self.oiz(a['score'])) for a in selection])

        summary = 'http://myanimelist.net/animelist/\x02%s\x02 | \x02%s\x02 days across \x02%d\x02 keions%s' % (user, days, len(consumed), showlist_string)
        self.irc.privmsg(message.source, summary, pretty=True)

    def common_keions(self, users):
        """ Get a list of tuples of keions that any two users have in common """
        ud_list = []
        for user in users:
            try:
                data = self.query('animelist/%s' % user)
            except urllib.error.HTTPError:
                return self.help_missing % user
            ud_list.append(data)

        common = []

        for a1 in ud_list[0]['anime']:
            for a2 in ud_list[1]['anime']:
                if a1['id'] == a2['id'] and a1['watched_status'] != 'plan to watch' and a2['watched_status'] != 'plan to watch':
                    common.append((a1, a2))

        return common

    def compare_self(self, message, args):
        """ Like a fight, but friendlier."""
        self.compare(message, {'user1': message.nick, 'user2': args['user']})

    def compare(self, message, args):
        users = [self.maluser(u) for u in [args['user1'], args['user2']]]
        common = self.common_keions(users)
        if type(common) == str:
            self.irc.privmsg(message.source, common, pretty=True)
            return

        total_score_diff = 0
        consensus = [] # animes scored the same
        both_scored = 0

        for a1, a2 in common:
            if (a1['score'] == a2['score']) and a1['score'] != 0:
                consensus.append(a1)
            if a1['score'] != 0 and a2['score'] != 0:
                both_scored += 1

        if len(consensus) > 0:
            selection = self.select(consensus)
            self.irc.privmsg(message.source,
                    '\x02%s\x02 and \x02%s\x02 | \x02%d\x02 common keions | agreement on \x02%d\x02/\x02%d\x02 mutually scored keions | %s' % (
                    users[0], users[1], len(common), len(consensus), both_scored,
                    ' | '.join(['%s : \x02%d\x02' % (a['title'], a['score']) for a in selection])),
                    pretty=True)
        else:
            # find the closest thing to common ground we have
            smallest_gap = 10
            closest = []

            for a1, a2 in common:
                if a1['score'] != 0 and a2['score'] != 0:
                    gap = fabs(a1['score'] - a2['score'])
                    if gap < smallest_gap:
                        smallest_gap = gap
                        closest = [(a1, a2)]
                    elif gap == smallest_gap:
                        closest.append((a1, a2))

            if len(closest) > 0:
                selection = self.select(closest)
                self.irc.privmsg(message.source, "\x02%s\x02 and \x02%s\x02 | \x02%d\x02 common keions | %s" % (
                        users[0], users[1], len(common),
                        ' | '.join(['%s : \x02%d\x02, \x02%d\x02' % (a[0]['title'], a[0]['score'], a[1]['score']) for a in selection])),
                        pretty=True)
            else:
                # we have no common ground :<
                selection = self.select(common)
                self.irc.privmsg(message.source, "\x02%s\x02 and \x02%s\x02 have \x02%d\x02 keions in common | %s" % (users[0], users[1],
                        len(common), ' | '.join([a[0]['title'] for a in selection])),
                        pretty=True)

    def fight_self(self, message, args):
        """ Fight someone.
        $<comchar>mal fight xinil
        >\x02colons\x02 vs. \x02xinil\x02\x03# |\x03 average contention\x03# :\x03 \x021.12\x02\x03# |\x03 Tengen Toppa Gurren Lagann\x03# :\x03 \x024\x02 vs. \x028\x02
        If you specify an additional username, you don't have to get involved yourself.
        $<comchar>mal fight xinil theshillito
        >\x02xinil\x02 vs. \x02theshillito\x02\x03# | \x03average contention\x03# :\x03 \x022.10\x02\x03# |\x03 Cowboy Bebop\x03# :\x03 \x0210\x02 vs. \x024\x02\x03# |\x03 Fate/stay night\x03# :\x03 \x027\x02 vs. \x021\x02
        """
        self.fight(message, {
            'user1': message.nick,
            'user2': args['user']
            })

    def fight(self, message, args):
        users = [self.maluser(u) for u in [args['user1'], args['user2']]]
        common = self.common_keions(users)
        if type(common) == str:
            self.irc.privmsg(message.source, common, pretty=True)
            return

        largest_gap = 0
        contention = []
        total_gap = 0
        considered = 0

        for a1, a2 in common:
            if a1['score'] != 0 and a2['score'] != 0:
                gap = fabs(a1['score'] - a2['score'])
                total_gap += gap
                considered += 1
                if gap > largest_gap:
                    largest_gap = gap
                    contention = [(a1, a2)]
                elif gap == largest_gap:
                    contention.append((a1, a2))

        if considered > 0:
            average_gap = total_gap/float(considered)

        if len(contention) > 0:
            selection = self.select(contention)
            self.irc.privmsg(message.source, "\x02%s\x02 vs. \x02%s\x02 | average contention : \x02%.2f\x02 | %s" % (
                    users[0], users[1], average_gap,
                    ' | '. join(['%s : \x02%d\x02 vs. \x02%d\x02' % (a[0]['title'], a[0]['score'], a[1]['score']) for a in selection])),
                    pretty=True)
        else:
            self.irc.privmsg(message.source, "\x02%s\x02 and \x02%s\x02 need to watch and score more stuff" % (users[0], users[1]),
                    pretty=True)

    def search(self, message, args):
        """ $<comchar>mal search churuya
        >\x02Nyoro-n Churuya-san\x02\x03# :\x03 ONA\x03# |\x03 http://myanimelist.net/anime/5957\x03# |\x03 An anime adaptation of the 4-panel strip manga release: Nyoron Churuya-san. Based on Suzumiya Haruhi's energetic and 'always up to go' character, Tsuruya."""
        query = args['query']

        search = self.query('anime/search', ['q=%s' % urllib.parse.quote(query)])

        # data = self.query('anime/%i' % search[0]['id'])
        try:
            keion = search[0]
        except IndexError:
            self.irc.privmsg(message.source,
                    'no results | http://myanimelist.net/anime.php?q=%s' % urllib.parse.quote(query),
                    pretty=True)
            return

        for show in search:
            if show['title'].lower() == args['query'].lower():
                keion = show

        keion_page = 'http://myanimelist.net/anime/%i' % show['id']

        # the html stripping should not be here, but it is for now because fuck you
        self.irc.privmsg(message.source,
                '\x02%s\x02 : %s | %s | %s' % (keion['title'], keion['type'], keion_page, re.sub('<[^<]+?>', '', keion['synopsis'])),
                pretty=True, crop=True)

    def query(self, query, additional_args=[]):
        args = '&'.join(self.default_args+additional_args)
        print(' -- '+self.url % (query, args))
        raw_data = urllib.request.urlopen(self.url % (query, args)).read().decode('utf-8')
        data = json.loads(raw_data)
        return data

    def correlate(self, id1, id2):
        print('hi')
<|endoftext|>


<commit_before>from jsonfield import JSONField
from hvad.models import TranslatableModel, TranslatedFields

from django.db import models
from django.contrib.sites.models import Site

class Placeholder(models.Model):

    name = models.SlugField(max_length=50)
    view_name = models.CharField(max_length=100, db_index=True,
                                 null=True, blank=True)
    widget_config = JSONField(default="[]")
    template_name = models.CharField(max_length=50, null=True, blank=True)

    # Foreign Keys
    site = models.ForeignKey('sites.Site')

    class Meta:
        unique_together = ('name', 'view_name')

    def __unicode__(self):
        return self.name

class Content(TranslatableModel):

    name = models.SlugField(max_length=50, unique=True)
    description = models.CharField(max_length=500, null=True, blank=True)
    url = models.CharField(max_length=255, null=True, blank=True)
    image = models.ImageField(upload_to='contents', null=True, blank=True)
    parent = models.ForeignKey('easy_cms.Content', null=True, blank=True,
                               related_name='children')
    created_at = models.DateTimeField(auto_now_add=True)
    template_name = models.CharField(max_length=50, null=True, blank=True)

    # Translations
    translations = TranslatedFields(
        title=models.CharField(max_length=255, null=True, blank=True),
        tagline=models.CharField(max_length=255, null=True, blank=True),
        content=models.TextField(null=True, blank=True)
    )

    # Foreign Keys
    site = models.ForeignKey('sites.Site')

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs):
        if self.parent:
            try:
                self.site
            except Site.DoesNotExist:
                self.site = self.parent.site
        for child in self.children.all():
            if child.site != self.site:
                child.site = self.site
                child.save()
        super(Content, self).save(*args, **kwargs)
<commit_msg>move some fields into translate<commit_after>from jsonfield import JSONField
from hvad.models import TranslatableModel, TranslatedFields

from django.db import models
from django.contrib.sites.models import Site

class Placeholder(models.Model):

    name = models.SlugField(max_length=50)
    view_name = models.CharField(max_length=100, db_index=True,
                                 null=True, blank=True)
    widget_config = JSONField(default="[]")
    template_name = models.CharField(max_length=50, null=True, blank=True)

    # Foreign Keys
    site = models.ForeignKey('sites.Site')

    class Meta:
        unique_together = ('name', 'view_name')

    def __unicode__(self):
        return self.name

class Content(TranslatableModel):

    name = models.SlugField(max_length=50, unique=True)
    image = models.ImageField(upload_to='contents', null=True, blank=True)
    parent = models.ForeignKey('easy_cms.Content', null=True, blank=True,
                               related_name='children')
    created_at = models.DateTimeField(auto_now_add=True)
    template_name = models.CharField(max_length=50, null=True, blank=True)

    # Translations
    translations = TranslatedFields(
        title=models.CharField(max_length=255, null=True, blank=True),
        tagline=models.CharField(max_length=255, null=True, blank=True),
        content=models.TextField(null=True, blank=True),
        description=models.CharField(max_length=500, null=True, blank=True),
        url=models.CharField(max_length=255, null=True, blank=True)
    )

    # Foreign Keys
    site = models.ForeignKey('sites.Site')

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs):
        if self.parent:
            try:
                self.site
            except Site.DoesNotExist:
                self.site = self.parent.site
        for child in self.children.all():
            if child.site != self.site:
                child.site = self.site
                child.save()
        super(Content, self).save(*args, **kwargs)
<|endoftext|>


<commit_before>import logging

import eventlet

from wampy.errors import ConnectionError, WampError, WampProtocolError
from wampy.messages import Message
from wampy.messages.hello import Hello
from wampy.messages.goodbye import Goodbye
from wampy.messages.yield_ import Yield
from wampy.transports.websocket.connection import WebSocket

from wampy.messages import MESSAGE_TYPE_MAP

logger = logging.getLogger('wampy.session')

def session_builder(client, router, realm, transport="websocket"):
    if transport == "websocket":
        transport = WebSocket(host=router.host, port=router.port)
        return Session(
            client=client, router=router, realm=realm, transport=transport
        )
    raise WampError("transport not supported: {}".format(transport))

class Session(object):
    """ A transient conversation between two Peers attached to a
    Realm and running over a Transport.

    WAMP Sessions are established over a WAMP Connection which is
    the responsibility of the ``Transport`` object.

    Each wampy ``Session`` manages its own WAMP connection via the
    ``Transport``.

    Once the connection is established, the Session is begun when
    the Realm is joined. This is achieved by sending the HELLO message.

    .. note::
        Routing occurs only between WAMP Sessions that have joined the
        same Realm.

    """

    def __init__(self, client, router, realm, transport):
        """ A Session between a Client and a Router.

        :Parameters:
            client : instance
                An instance of :class:`peers.Client`.
            router : instance
                An instance of :class:`peers.Router`.
            realm : str
                The name of the Realm on the ``router`` to join.
            transport : instance
                An instance of :class:`transports.Transport`.

        """
        self.client = client
        self.router = router
        self.realm = realm
        self.transport = transport

        self.subscription_map = {}
        self.registration_map = {}

        self.session_id = None
        # spawn a green thread to listen for incoming messages over
        # a connection and put them on a queue to be processed
        self._connection = None
        self._managed_thread = None
        self._message_queue = eventlet.Queue()

    @property
    def host(self):
        return self.router.host

    @property
    def port(self):
        return self.router.port

    @property
    def roles(self):
        return self.client.roles

    # backwards (or maybe forwards!) compat
    @property
    def id(self):
        return self.session_id

    def begin(self):
        self._connect()
        self._say_hello()

    def end(self):
        self._say_goodbye()
        self._disconnet()
        self.subscription_map = {}
        self.registration_map = {}
        self.session_id = None

    def send_message(self, message):
        message_type = MESSAGE_TYPE_MAP[message.WAMP_CODE]
        message = message.serialize()

        logger.debug(
            'sending "%s" message: %s', message_type, message
        )

        self._connection.send_websocket_frame(str(message))

    def recv_message(self, timeout=5):
        logger.debug('waiting for message')

        try:
            message = self._wait_for_message(timeout)
        except eventlet.Timeout:
            raise WampProtocolError("no message returned")

        logger.debug(
            'received message: "%s"', MESSAGE_TYPE_MAP[message[0]]
        )

        return message

    def handle_message(self, message):
        logger.info(
            "%s received message: %s",
            self.client.id, MESSAGE_TYPE_MAP[message[0]]
        )

        wamp_code = message[0]
        if wamp_code == Message.REGISTERED:  # 64
            self._message_queue.put(message)

        elif wamp_code == Message.INVOCATION:  # 68
            args = []
            kwargs = {}

            try:
                # no args, no kwargs
                _, request_id, registration_id, details = message
            except ValueError:
                # args, no kwargs
                try:
                    _, request_id, registration_id, details, args = message
                except ValueError:
                    # args and kwargs
                    _, request_id, registration_id, details, args, kwargs = (
                        message)

            registration_id_procedure_name_map = {
                v: k for k, v in self.registration_map.items()
            }

            procedure_name = registration_id_procedure_name_map[
                registration_id]

            entrypoint = getattr(self.client, procedure_name)
            resp = entrypoint(*args, **kwargs)
            result_args = [resp]

            result_kwargs = {'_meta': {}}
            result_kwargs['_meta']['procedure_name'] = procedure_name
            result_kwargs['_meta']['session_id'] = self.session_id
            result_kwargs['_meta']['client_id'] = self.client.id

            message = Yield(
                request_id, result_args=result_args, result_kwargs=result_kwargs
            )
            self.send_message(message)

        elif wamp_code == Message.GOODBYE:  # 6
            _, _, response_message = message
            assert response_message == 'wamp.close.normal'
            self._message_queue.put(message)

        elif wamp_code == Message.RESULT:  # 50
            self._message_queue.put(message)

        elif wamp_code == Message.WELCOME:  # 2
            _, session_id, _ = message
            self.session_id = session_id

            self._message_queue.put(message)

        elif wamp_code == Message.ERROR:
            _, _, _, _, _, errors = message
            logger.error(errors)
            self._message_queue.put(message)

        elif wamp_code == Message.SUBSCRIBED:
            self._message_queue.put(message)

        elif wamp_code == Message.EVENT:
            payload_list = []
            payload_dict = {}

            try:
                # [
                #   EVENT,
                #   SUBSCRIBED.Subscription|id,
                #   PUBLISHED.Publication|id,
                #   Details|dict,
                #   PUBLISH.Arguments|list,
                #   PUBLISH.ArgumentKw|dict]
                # ]
                _, subscription_id, _, details, payload_list, payload_dict = (
                    message)
            except ValueError:

                try:
                    # [
                    #   EVENT,
                    #   SUBSCRIBED.Subscription|id,
                    #   PUBLISHED.Publication|id,
                    #   Details|dict,
                    #   PUBLISH.Arguments|list,
                    # ]
                    _, subscription_id, _, details, payload_list = message
                except ValueError:
                    # [
                    #   EVENT,
                    #   SUBSCRIBED.Subscription|id,
                    #   PUBLISHED.Publication|id,
                    #   Details|dict,
                    # ]
                    _, subscription_id, _, details = message

            func_name, topic = self.subscription_map[subscription_id]
            try:
                func = getattr(self.client, func_name)
            except AttributeError:
                raise WampError(
                    "Event handler not found: {}".format(func_name)
                )

            payload_dict['_meta'] = {}
            payload_dict['_meta']['topic'] = topic
            payload_dict['_meta']['subscription_id'] = subscription_id

            func(*payload_list, **payload_dict)

        else:
            logger.warning(
                'unhandled message: "%s"', message
            )

    def _connect(self):
        connection = self.transport

        try:
            connection.connect()
        except Exception as exc:
            raise ConnectionError(exc)

        self._listen_on_connection(connection, self._message_queue)
        self._connection = connection

    def _disconnet(self):
        self._managed_thread.kill()
        self._connection = None
        self.session = None

        logger.debug('disconnected from %s', self.host)

    def _say_hello(self):
        message = Hello(self.realm, self.roles)
        self.send_message(message)
        response = self.recv_message()

        # response message must be either WELCOME or ABORT
        wamp_code, session_id, _ = response
        if wamp_code not in [Message.WELCOME, Message.ABORT]:
            raise WampError(
                'unexpected response from HELLO message: {}'.format(
                    response
                )
            )

        self.session_id = session_id
        return response

    def _say_goodbye(self):
        message = Goodbye()
        try:
            self.send_message(message)
        except Exception as exc:
            # we can't be sure what the Exception is here because it will
            # be from the Router implementation
            logger.warning("GOODBYE failed!: %s", exc)
        else:
            try:
                message = self.recv_message(timeout=2)
                if message[0] != Message.GOODBYE:
                    raise WampProtocolError(
                        "Unexpected response from GOODBYE message: {}".format(
                            message
                        )
                    )
            except WampProtocolError:
                # Server already gone away?
                pass

    def _listen_on_connection(self, connection, message_queue):
        def connection_handler():
            while True:
                try:
                    frame = connection.read_websocket_frame()
                    if frame:
                        message = frame.payload
                        self.handle_message(message)
                except (
                        SystemExit, KeyboardInterrupt, ConnectionError,
                        WampProtocolError,
                ):
                    break

        gthread = eventlet.spawn(connection_handler)
        self._managed_thread = gthread

    def _wait_for_message(self, timeout):
        q = self._message_queue

        with eventlet.Timeout(timeout):
            while q.qsize() == 0:
                # if the expected message is not there, switch context to
                # allow other threads to continue working to fetch it for us
                eventlet.sleep()

        message = q.get()
        return message
<commit_msg>tidy up<commit_after>import logging

import eventlet

from wampy.errors import ConnectionError, WampError, WampProtocolError
from wampy.messages import Message
from wampy.messages.hello import Hello
from wampy.messages.goodbye import Goodbye
from wampy.messages.yield_ import Yield
from wampy.transports.websocket.connection import WebSocket

from wampy.messages import MESSAGE_TYPE_MAP

logger = logging.getLogger('wampy.session')

def session_builder(client, router, realm, transport="websocket"):
    if transport == "websocket":
        transport = WebSocket(host=router.host, port=router.port)
        return Session(
            client=client, router=router, realm=realm, transport=transport
        )
    raise WampError("transport not supported: {}".format(transport))

class Session(object):
    """ A transient conversation between two Peers attached to a
    Realm and running over a Transport.

    WAMP Sessions are established over a WAMP Connection which is
    the responsibility of the ``Transport`` object.

    Each wampy ``Session`` manages its own WAMP connection via the
    ``Transport``.

    Once the connection is established, the Session is begun when
    the Realm is joined. This is achieved by sending the HELLO message.

    .. note::
        Routing occurs only between WAMP Sessions that have joined the
        same Realm.

    """

    def __init__(self, client, router, realm, transport):
        """ A Session between a Client and a Router.

        :Parameters:
            client : instance
                An instance of :class:`peers.Client`.
            router : instance
                An instance of :class:`peers.Router`.
            realm : str
                The name of the Realm on the ``router`` to join.
            transport : instance
                An instance of :class:`transports.Transport`.

        """
        self.client = client
        self.router = router
        self.realm = realm
        self.transport = transport

        self.subscription_map = {}
        self.registration_map = {}

        self.session_id = None
        # spawn a green thread to listen for incoming messages over
        # a connection and put them on a queue to be processed
        self._connection = None
        self._managed_thread = None
        self._message_queue = eventlet.Queue()

    @property
    def host(self):
        return self.router.host

    @property
    def port(self):
        return self.router.port

    @property
    def roles(self):
        return self.client.roles

    # backwards (or maybe forwards!) compat
    @property
    def id(self):
        return self.session_id

    def begin(self):
        self._connect()
        self._say_hello()

    def end(self):
        self._say_goodbye()
        self._disconnet()
        self.subscription_map = {}
        self.registration_map = {}
        self.session_id = None

    def send_message(self, message):
        message_type = MESSAGE_TYPE_MAP[message.WAMP_CODE]
        message = message.serialize()

        logger.debug(
            'sending "%s" message: %s', message_type, message
        )

        self._connection.send_websocket_frame(str(message))

    def recv_message(self, timeout=5):
        logger.debug('waiting for message')

        try:
            message = self._wait_for_message(timeout)
        except eventlet.Timeout:
            raise WampProtocolError("no message returned")

        logger.debug(
            'received message: "%s"', MESSAGE_TYPE_MAP[message[0]]
        )

        return message

    def handle_message(self, message):
        logger.info(
            "%s received message: %s",
            self.client.id, MESSAGE_TYPE_MAP[message[0]]
        )

        wamp_code = message[0]
        if wamp_code == Message.REGISTERED:  # 64
            self._message_queue.put(message)

        elif wamp_code == Message.INVOCATION:  # 68
            args = []
            kwargs = {}

            try:
                # no args, no kwargs
                _, request_id, registration_id, details = message
            except ValueError:
                # args, no kwargs
                try:
                    _, request_id, registration_id, details, args = message
                except ValueError:
                    # args and kwargs
                    _, request_id, registration_id, details, args, kwargs = (
                        message)

            registration_id_procedure_name_map = {
                v: k for k, v in self.registration_map.items()
            }

            procedure_name = registration_id_procedure_name_map[
                registration_id]

            entrypoint = getattr(self.client, procedure_name)
            resp = entrypoint(*args, **kwargs)
            result_args = [resp]

            result_kwargs = {'_meta': {}}
            result_kwargs['_meta']['procedure_name'] = procedure_name
            result_kwargs['_meta']['session_id'] = self.session_id
            result_kwargs['_meta']['client_id'] = self.client.id

            message = Yield(
                request_id,
                result_args=result_args,
                result_kwargs=result_kwargs,
            )
            self.send_message(message)

        elif wamp_code == Message.GOODBYE:  # 6
            _, _, response_message = message
            self._message_queue.put(message)

        elif wamp_code == Message.RESULT:  # 50
            self._message_queue.put(message)

        elif wamp_code == Message.WELCOME:  # 2
            _, session_id, _ = message
            self.session_id = session_id
            self._message_queue.put(message)

        elif wamp_code == Message.ERROR:
            _, _, _, _, _, errors = message
            logger.error(errors)
            self._message_queue.put(message)

        elif wamp_code == Message.SUBSCRIBED:
            self._message_queue.put(message)

        elif wamp_code == Message.EVENT:
            payload_list = []
            payload_dict = {}

            try:
                # [
                #   EVENT,
                #   SUBSCRIBED.Subscription|id,
                #   PUBLISHED.Publication|id,
                #   Details|dict,
                #   PUBLISH.Arguments|list,
                #   PUBLISH.ArgumentKw|dict]
                # ]
                _, subscription_id, _, details, payload_list, payload_dict = (
                    message)
            except ValueError:

                try:
                    # [
                    #   EVENT,
                    #   SUBSCRIBED.Subscription|id,
                    #   PUBLISHED.Publication|id,
                    #   Details|dict,
                    #   PUBLISH.Arguments|list,
                    # ]
                    _, subscription_id, _, details, payload_list = message
                except ValueError:
                    # [
                    #   EVENT,
                    #   SUBSCRIBED.Subscription|id,
                    #   PUBLISHED.Publication|id,
                    #   Details|dict,
                    # ]
                    _, subscription_id, _, details = message

            func_name, topic = self.subscription_map[subscription_id]
            try:
                func = getattr(self.client, func_name)
            except AttributeError:
                raise WampError(
                    "Event handler not found: {}".format(func_name)
                )

            payload_dict['_meta'] = {}
            payload_dict['_meta']['topic'] = topic
            payload_dict['_meta']['subscription_id'] = subscription_id

            func(*payload_list, **payload_dict)

        else:
            logger.warning(
                'unhandled message: "%s"', message
            )

    def _connect(self):
        connection = self.transport

        try:
            connection.connect()
        except Exception as exc:
            raise ConnectionError(exc)

        self._listen_on_connection(connection, self._message_queue)
        self._connection = connection

    def _disconnet(self):
        self._managed_thread.kill()
        self._connection = None
        self.session = None

        logger.debug('disconnected from %s', self.host)

    def _say_hello(self):
        message = Hello(self.realm, self.roles)
        self.send_message(message)
        response = self.recv_message()

        # response message must be either WELCOME or ABORT
        wamp_code, session_id, _ = response
        if wamp_code not in [Message.WELCOME, Message.ABORT]:
            raise WampError(
                'unexpected response from HELLO message: {}'.format(
                    response
                )
            )

        self.session_id = session_id
        return response

    def _say_goodbye(self):
        message = Goodbye()
        try:
            self.send_message(message)
        except Exception as exc:
            # we can't be sure what the Exception is here because it will
            # be from the Router implementation
            logger.warning("GOODBYE failed!: %s", exc)
        else:
            try:
                message = self.recv_message(timeout=2)
                if message[0] != Message.GOODBYE:
                    raise WampProtocolError(
                        "Unexpected response from GOODBYE message: {}".format(
                            message
                        )
                    )
            except WampProtocolError:
                # Server already gone away?
                pass

    def _listen_on_connection(self, connection, message_queue):
        def connection_handler():
            while True:
                try:
                    frame = connection.read_websocket_frame()
                    if frame:
                        message = frame.payload
                        self.handle_message(message)
                except (
                        SystemExit, KeyboardInterrupt, ConnectionError,
                        WampProtocolError,
                ):
                    break

        gthread = eventlet.spawn(connection_handler)
        self._managed_thread = gthread

    def _wait_for_message(self, timeout):
        q = self._message_queue

        with eventlet.Timeout(timeout):
            while q.qsize() == 0:
                # if the expected message is not there, switch context to
                # allow other threads to continue working to fetch it for us
                eventlet.sleep()

        message = q.get()
        return message
<|endoftext|>


<commit_before>class Reference(object):
    def __init__(self, thing, element):
        self.thing, self.element = thing, element

    @property
    def thing_index(self):
        return self.thing.index

    @property
    def element_index(self):
        return self.element.index

    @property
    def convention(self):
        return self.element.convention
<commit_msg>Added 2 additional reference types - Local and Static<commit_after>class Reference(object):

    def __init__(self, type):
        super().__init__()
        self._type = type

    @property
    def type(self):
        return self._type

class ElementReference(Reference):
    def __init__(self, thing, element, instance):
        super(ElementReference, self).__init__(element.type)
        self.thing, self.element, self.instance = thing, element, instance

    @property
    def thing_index(self):
        return self.thing.index

    @property
    def element_index(self):
        return self.element.index

    @property
    def convention(self):
        return self.element.convention

    @property
    def static(self):
        return self.element.static

class LocalReference(Reference):
    def __init__(self, local):
        super(LocalReference, self).__init__(local.type)
        self.local = local

    @property
    def local_index(self):
        return self.local.index

class StaticReference(Reference):
    def __init__(self, value):
        super(StaticReference, self).__init__(value.type)
        self.value = value

<|endoftext|>


<commit_before>from __future__ import absolute_import
import numpy as np
from . import utils
from . import nmf
from . import mdl
from . import compression

def bicluster(array, n=None, share_elements=True, comp_level=None):
    if n is None:
        n_iters = array.shape[1]
        online_mdl = mdl.OnlineMDL()
    else:
        n_iters = n

    downdater = utils.Downdater(array)

    bic_list = []
    total_codelength = []
    for i in range(n_iters):
        if downdater.array.nnz == 0:
            break

        print(i)

        u, v = single_bicluster(downdater.array, comp_level=comp_level)

        if u.nnz <= 1 or v.nnz <= 1:
            break

        bic_list.append((u, v))

        idx_v = utils.find(v)[1]
        downdater.remove_columns(idx_v)
        if not share_elements:
            idx_u = utils.find(u)[0]
            downdater.remove_rows(idx_u)

        if n is None:
            cl = online_mdl.add_rank1_approximation(downdater.array, u, v)
            total_codelength.append(cl)

    if n is None and bic_list:
        cut_point = np.argmin(np.array(total_codelength))
        bic_list = bic_list[:cut_point+1]

    return bic_list

def single_bicluster(array, comp_level=None):
    if comp_level is not None:
        array_nmf = compression.compress_columns(array, comp_level)
    else:
        array_nmf = array
    u, v = nmf.nmf_robust_multiplicative(array_nmf, 1)
    u = utils.binarize(u)

    idx_u = utils.find(u)[0]
    array_crop, u_crop, v_init = crop_left(array, idx_u, comp_level=comp_level)
    v = nmf.nmf_robust_admm(array_crop, u_init=u_crop, v_init=v_init,
                            update='right')
    v = utils.binarize(v)

    idx_v = utils.find(v)[1]
    array_crop, u_init, v_crop = crop_right(array, idx_v, comp_level=comp_level)
    u = nmf.nmf_robust_admm(array_crop, u_init=u_init, v_init=v_crop,
                            update='left')
    u = utils.binarize(u)
    return u, v

def crop_left(array, idx_u, comp_level=None):
    array_crop = utils.sparse(array[idx_u, :])

    if comp_level is not None and comp_level < array_crop.shape[0]:
        array_crop = compression.compress_rows(array_crop, comp_level)

    u_crop = utils.sparse(np.ones((array_crop.shape[0], 1)))
    v_init = u_crop.T.dot(array_crop)
    arr_sum = array_crop.sum(axis=0)
    v_init[arr_sum > 0] /= arr_sum[arr_sum > 0]
    return array_crop, u_crop, v_init

def crop_right(array, idx_v, comp_level=None):
    array_crop = utils.sparse(array[:, idx_v])

    if comp_level is not None and comp_level < array_crop.shape[1]:
        array_crop = compression.compress_columns(array_crop, comp_level)

    v_crop = utils.sparse(np.ones((1, array_crop.shape[1])))
    u_init = array_crop.dot(v_crop.T)
    arr_sum = array_crop.sum(axis=1)
    u_init[arr_sum > 0] /= arr_sum[arr_sum > 0]
    return array_crop, u_init, v_crop
<commit_msg>Removed print statement<commit_after>from __future__ import absolute_import
import numpy as np
from . import utils
from . import nmf
from . import mdl
from . import compression

def bicluster(array, n=None, share_elements=True, comp_level=None):
    if n is None:
        n_iters = array.shape[1]
        online_mdl = mdl.OnlineMDL()
    else:
        n_iters = n

    downdater = utils.Downdater(array)

    bic_list = []
    total_codelength = []
    for _ in range(n_iters):
        if downdater.array.nnz == 0:
            break

        u, v = single_bicluster(downdater.array, comp_level=comp_level)

        if u.nnz <= 1 or v.nnz <= 1:
            break

        bic_list.append((u, v))

        idx_v = utils.find(v)[1]
        downdater.remove_columns(idx_v)
        if not share_elements:
            idx_u = utils.find(u)[0]
            downdater.remove_rows(idx_u)

        if n is None:
            cl = online_mdl.add_rank1_approximation(downdater.array, u, v)
            total_codelength.append(cl)

    if n is None and bic_list:
        cut_point = np.argmin(np.array(total_codelength))
        bic_list = bic_list[:cut_point+1]

    return bic_list

def single_bicluster(array, comp_level=None):
    if comp_level is not None:
        array_nmf = compression.compress_columns(array, comp_level)
    else:
        array_nmf = array
    u, v = nmf.nmf_robust_multiplicative(array_nmf, 1)
    u = utils.binarize(u)

    idx_u = utils.find(u)[0]
    array_crop, u_crop, v_init = crop_left(array, idx_u, comp_level=comp_level)
    v = nmf.nmf_robust_admm(array_crop, u_init=u_crop, v_init=v_init,
                            update='right')
    v = utils.binarize(v)

    idx_v = utils.find(v)[1]
    array_crop, u_init, v_crop = crop_right(array, idx_v, comp_level=comp_level)
    u = nmf.nmf_robust_admm(array_crop, u_init=u_init, v_init=v_crop,
                            update='left')
    u = utils.binarize(u)
    return u, v

def crop_left(array, idx_u, comp_level=None):
    array_crop = utils.sparse(array[idx_u, :])

    if comp_level is not None and comp_level < array_crop.shape[0]:
        array_crop = compression.compress_rows(array_crop, comp_level)

    u_crop = utils.sparse(np.ones((array_crop.shape[0], 1)))
    v_init = u_crop.T.dot(array_crop)
    arr_sum = array_crop.sum(axis=0)
    v_init[arr_sum > 0] /= arr_sum[arr_sum > 0]
    return array_crop, u_crop, v_init

def crop_right(array, idx_v, comp_level=None):
    array_crop = utils.sparse(array[:, idx_v])

    if comp_level is not None and comp_level < array_crop.shape[1]:
        array_crop = compression.compress_columns(array_crop, comp_level)

    v_crop = utils.sparse(np.ones((1, array_crop.shape[1])))
    u_init = array_crop.dot(v_crop.T)
    arr_sum = array_crop.sum(axis=1)
    u_init[arr_sum > 0] /= arr_sum[arr_sum > 0]
    return array_crop, u_init, v_crop
<|endoftext|>


<commit_before>"""
Views for creating, editing and viewing site-specific user profiles.

"""

from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.http import Http404
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import get_object_or_404
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.views.generic.list_detail import object_list

from profiles import utils

def create_profile(request, form_class=None, success_url=None,
                   template_name='profiles/create_profile.html',
                   extra_context=None):
    """
    Create a profile for the current user, if one doesn't already
    exist.

    If the user already has a profile, as determined by
    ``request.user.get_profile()``, a redirect will be issued to the
    :view:`profiles.views.edit_profile` view. If no profile model has
    been specified in the ``AUTH_PROFILE_MODULE`` setting,
    ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
    raised.

    **Optional arguments:**

    ``extra_context``
        A dictionary of variables to add to the template context. Any
        callable object in this dictionary will be called to produce
        the end result which appears in the context.

    ``form_class``
        The form class to use for validating and creating the user
        profile. This form class must define a method named
        ``save()``, implementing the same argument signature as the
        ``save()`` method of a standard Django ``ModelForm`` (this
        view will call ``save(commit=False)`` to obtain the profile
        object, and fill in the user before the final save). If the
        profile object includes many-to-many relations, the convention
        established by ``ModelForm`` of using a method named
        ``save_m2m()`` will be used, and so your form class should
        also define this method.

        If this argument is not supplied, this view will use a
        ``ModelForm`` automatically generated from the model specified
        by ``AUTH_PROFILE_MODULE``.

    ``success_url``
        The URL to redirect to after successful profile creation. If
        this argument is not supplied, this will default to the URL of
        :view:`profiles.views.profile_detail` for the newly-created
        profile object.

    ``template_name``
        The template to use when displaying the profile-creation
        form. If not supplied, this will default to
        :template:`profiles/create_profile.html`.

    **Context:**

    ``form``
        The profile-creation form.

    **Template:**

    ``template_name`` keyword argument, or
    :template:`profiles/create_profile.html`.

    """
    try:
        profile_obj = request.user.get_profile()
        return HttpResponseRedirect(reverse('profiles_edit_profile'))
    except ObjectDoesNotExist:
        pass

    #
    # We set up success_url here, rather than as the default value for
    # the argument. Trying to do it as the argument's default would
    # mean evaluating the call to reverse() at the time this module is
    # first imported, which introduces a circular dependency: to
    # perform the reverse lookup we need access to profiles/urls.py,
    # but profiles/urls.py in turn imports this module.
    #

    if success_url is None:
        success_url = reverse('profiles_profile_detail',
                              kwargs={ 'username': request.user.username })
    if form_class is None:
        form_class = utils.get_profile_form()
    if request.method == 'POST':
        form = form_class(data=request.POST, files=request.FILES)
        if form.is_valid():
            profile_obj = form.save(commit=False)
            profile_obj.user = request.user
            profile_obj.save()
            if hasattr(form, 'save_m2m'):
                form.save_m2m()
            return HttpResponseRedirect(success_url)
    else:
        form = form_class()

    if extra_context is None:
        extra_context = {}
    context = RequestContext(request)
    for key, value in extra_context.items():
        context[key] = callable(value) and value() or value

    return render_to_response(template_name,
                              { 'form': form },
                              context_instance=context)
create_profile = login_required(create_profile)

def edit_profile(request, form_class=None, success_url=None,
                 template_name='profiles/edit_profile.html',
                 extra_context=None):
    """
    Edit the current user's profile.

    If the user does not already have a profile (as determined by
    ``User.get_profile()``), a redirect will be issued to the
    :view:`profiles.views.create_profile` view; if no profile model
    has been specified in the ``AUTH_PROFILE_MODULE`` setting,
    ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
    raised.

    **Optional arguments:**

    ``extra_context``
        A dictionary of variables to add to the template context. Any
        callable object in this dictionary will be called to produce
        the end result which appears in the context.

    ``form_class``
        The form class to use for validating and editing the user
        profile. This form class must operate similarly to a standard
        Django ``ModelForm`` in that it must accept an instance of the
        object to be edited as the keyword argument ``instance`` to
        its constructor, and it must implement a method named
        ``save()`` which will save the updates to the object. If this
        argument is not specified, this view will use a ``ModelForm``
        generated from the model specified in the
        ``AUTH_PROFILE_MODULE`` setting.

    ``success_url``
        The URL to redirect to following a successful edit. If not
        specified, this will default to the URL of
        :view:`profiles.views.profile_detail` for the profile object
        being edited.

    ``template_name``
        The template to use when displaying the profile-editing
        form. If not specified, this will default to
        :template:`profiles/edit_profile.html`.

    **Context:**

    ``form``
        The form for editing the profile.

    ``profile``
         The user's current profile.

    **Template:**

    ``template_name`` keyword argument or
    :template:`profiles/edit_profile.html`.

    """
    try:
        profile_obj = request.user.get_profile()
    except ObjectDoesNotExist:
        return HttpResponseRedirect(reverse('profiles_create_profile'))

    #
    # See the comment in create_profile() for discussion of why
    # success_url is set up here, rather than as a default value for
    # the argument.
    #

    if success_url is None:
        success_url = reverse('profiles_profile_detail',
                              kwargs={ 'username': request.user.username })
    if form_class is None:
        form_class = utils.get_profile_form()
    if request.method == 'POST':
        form = form_class(data=request.POST, files=request.FILES, instance=profile_obj)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(success_url)
    else:
        form = form_class(instance=profile_obj)

    if extra_context is None:
        extra_context = {}
    context = RequestContext(request)
    for key, value in extra_context.items():
        context[key] = callable(value) and value() or value

    return render_to_response(template_name,
                              { 'form': form,
                                'profile': profile_obj, },
                              context_instance=context)
edit_profile = login_required(edit_profile)

def profile_detail(request, username, public_profile_field=None,
                   template_name='profiles/profile_detail.html',
                   extra_context=None):
    """
    Detail view of a user's profile.

    If no profile model has been specified in the
    ``AUTH_PROFILE_MODULE`` setting,
    ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
    raised.

    If the user has not yet created a profile, ``Http404`` will be
    raised.

    **Required arguments:**

    ``username``
        The username of the user whose profile is being displayed.

    **Optional arguments:**

    ``extra_context``
        A dictionary of variables to add to the template context. Any
        callable object in this dictionary will be called to produce
        the end result which appears in the context.

    ``public_profile_field``
        The name of a ``BooleanField`` on the profile model; if the
        value of that field on the user's profile is ``False``, the
        ``profile`` variable in the template will be ``None``. Use
        this feature to allow users to mark their profiles as not
        being publicly viewable.

        If this argument is not specified, it will be assumed that all
        users' profiles are publicly viewable.

    ``template_name``
        The name of the template to use for displaying the profile. If
        not specified, this will default to
        :template:`profiles/profile_detail.html`.

    **Context:**

    ``profile``
        The user's profile, or ``None`` if the user's profile is not
        publicly viewable (see the description of
        ``public_profile_field`` above).

    **Template:**

    ``template_name`` keyword argument or
    :template:`profiles/profile_detail.html`.

    """
    user = get_object_or_404(User, username=username)
    try:
        profile_obj = user.get_profile()
    except ObjectDoesNotExist:
        return HttpResponseRedirect(reverse('create_profile', kwargs={'user': request.user}))

    #profile_obj = user.get_profile()

    if public_profile_field is not None and \
       not getattr(profile_obj, public_profile_field):
        profile_obj = None

    if extra_context is None:
        extra_context = {}
    context = RequestContext(request)
    for key, value in extra_context.items():
        context[key] = callable(value) and value() or value

    return render_to_response(template_name,
                              { 'profile': profile_obj },
                              context_instance=context)

def profile_list(request, public_profile_field=None,
                 template_name='profiles/profile_list.html', **kwargs):
    """
    A list of user profiles.

    If no profile model has been specified in the
    ``AUTH_PROFILE_MODULE`` setting,
    ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
    raised.

    **Optional arguments:**

    ``public_profile_field``
        The name of a ``BooleanField`` on the profile model; if the
        value of that field on a user's profile is ``False``, that
        profile will be excluded from the list. Use this feature to
        allow users to mark their profiles as not being publicly
        viewable.

        If this argument is not specified, it will be assumed that all
        users' profiles are publicly viewable.

    ``template_name``
        The name of the template to use for displaying the profiles. If
        not specified, this will default to
        :template:`profiles/profile_list.html`.

    Additionally, all arguments accepted by the
    :view:`django.views.generic.list_detail.object_list` generic view
    will be accepted here, and applied in the same fashion, with one
    exception: ``queryset`` will always be the ``QuerySet`` of the
    model specified by the ``AUTH_PROFILE_MODULE`` setting, optionally
    filtered to remove non-publicly-viewable proiles.

    **Context:**

    Same as the :view:`django.views.generic.list_detail.object_list`
    generic view.

    **Template:**

    ``template_name`` keyword argument or
    :template:`profiles/profile_list.html`.

    """
    profile_model = utils.get_profile_model()
    queryset = profile_model._default_manager.all()
    if public_profile_field is not None:
        queryset = queryset.filter(**{ public_profile_field: True })
    kwargs['queryset'] = queryset
    return object_list(request, template_name=template_name, **kwargs)
<commit_msg>trying a straight view<commit_after>"""
Views for creating, editing and viewing site-specific user profiles.

"""

from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.http import Http404
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import get_object_or_404
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.views.generic.list_detail import object_list

from profiles import utils

def create_profile(request, form_class=None, success_url=None,
                   template_name='profiles/create_profile.html',
                   extra_context=None):
    """
    Create a profile for the current user, if one doesn't already
    exist.

    If the user already has a profile, as determined by
    ``request.user.get_profile()``, a redirect will be issued to the
    :view:`profiles.views.edit_profile` view. If no profile model has
    been specified in the ``AUTH_PROFILE_MODULE`` setting,
    ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
    raised.

    **Optional arguments:**

    ``extra_context``
        A dictionary of variables to add to the template context. Any
        callable object in this dictionary will be called to produce
        the end result which appears in the context.

    ``form_class``
        The form class to use for validating and creating the user
        profile. This form class must define a method named
        ``save()``, implementing the same argument signature as the
        ``save()`` method of a standard Django ``ModelForm`` (this
        view will call ``save(commit=False)`` to obtain the profile
        object, and fill in the user before the final save). If the
        profile object includes many-to-many relations, the convention
        established by ``ModelForm`` of using a method named
        ``save_m2m()`` will be used, and so your form class should
        also define this method.

        If this argument is not supplied, this view will use a
        ``ModelForm`` automatically generated from the model specified
        by ``AUTH_PROFILE_MODULE``.

    ``success_url``
        The URL to redirect to after successful profile creation. If
        this argument is not supplied, this will default to the URL of
        :view:`profiles.views.profile_detail` for the newly-created
        profile object.

    ``template_name``
        The template to use when displaying the profile-creation
        form. If not supplied, this will default to
        :template:`profiles/create_profile.html`.

    **Context:**

    ``form``
        The profile-creation form.

    **Template:**

    ``template_name`` keyword argument, or
    :template:`profiles/create_profile.html`.

    """
    try:
        profile_obj = request.user.get_profile()
        return HttpResponseRedirect(reverse('profiles_edit_profile'))
    except ObjectDoesNotExist:
        pass

    #
    # We set up success_url here, rather than as the default value for
    # the argument. Trying to do it as the argument's default would
    # mean evaluating the call to reverse() at the time this module is
    # first imported, which introduces a circular dependency: to
    # perform the reverse lookup we need access to profiles/urls.py,
    # but profiles/urls.py in turn imports this module.
    #

    if success_url is None:
        success_url = reverse('profiles_profile_detail',
                              kwargs={ 'username': request.user.username })
    if form_class is None:
        form_class = utils.get_profile_form()
    if request.method == 'POST':
        form = form_class(data=request.POST, files=request.FILES)
        if form.is_valid():
            profile_obj = form.save(commit=False)
            profile_obj.user = request.user
            profile_obj.save()
            if hasattr(form, 'save_m2m'):
                form.save_m2m()
            return HttpResponseRedirect(success_url)
    else:
        form = form_class()

    if extra_context is None:
        extra_context = {}
    context = RequestContext(request)
    for key, value in extra_context.items():
        context[key] = callable(value) and value() or value

    return render_to_response(template_name,
                              { 'form': form },
                              context_instance=context)
create_profile = login_required(create_profile)

def edit_profile(request, form_class=None, success_url=None,
                 template_name='profiles/edit_profile.html',
                 extra_context=None):
    """
    Edit the current user's profile.

    If the user does not already have a profile (as determined by
    ``User.get_profile()``), a redirect will be issued to the
    :view:`profiles.views.create_profile` view; if no profile model
    has been specified in the ``AUTH_PROFILE_MODULE`` setting,
    ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
    raised.

    **Optional arguments:**

    ``extra_context``
        A dictionary of variables to add to the template context. Any
        callable object in this dictionary will be called to produce
        the end result which appears in the context.

    ``form_class``
        The form class to use for validating and editing the user
        profile. This form class must operate similarly to a standard
        Django ``ModelForm`` in that it must accept an instance of the
        object to be edited as the keyword argument ``instance`` to
        its constructor, and it must implement a method named
        ``save()`` which will save the updates to the object. If this
        argument is not specified, this view will use a ``ModelForm``
        generated from the model specified in the
        ``AUTH_PROFILE_MODULE`` setting.

    ``success_url``
        The URL to redirect to following a successful edit. If not
        specified, this will default to the URL of
        :view:`profiles.views.profile_detail` for the profile object
        being edited.

    ``template_name``
        The template to use when displaying the profile-editing
        form. If not specified, this will default to
        :template:`profiles/edit_profile.html`.

    **Context:**

    ``form``
        The form for editing the profile.

    ``profile``
         The user's current profile.

    **Template:**

    ``template_name`` keyword argument or
    :template:`profiles/edit_profile.html`.

    """
    try:
        profile_obj = request.user.get_profile()
    except ObjectDoesNotExist:
        return HttpResponseRedirect(reverse('profiles_create_profile'))

    #
    # See the comment in create_profile() for discussion of why
    # success_url is set up here, rather than as a default value for
    # the argument.
    #

    if success_url is None:
        success_url = reverse('profiles_profile_detail',
                              kwargs={ 'username': request.user.username })
    if form_class is None:
        form_class = utils.get_profile_form()
    if request.method == 'POST':
        form = form_class(data=request.POST, files=request.FILES, instance=profile_obj)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(success_url)
    else:
        form = form_class(instance=profile_obj)

    if extra_context is None:
        extra_context = {}
    context = RequestContext(request)
    for key, value in extra_context.items():
        context[key] = callable(value) and value() or value

    return render_to_response(template_name,
                              { 'form': form,
                                'profile': profile_obj, },
                              context_instance=context)
edit_profile = login_required(edit_profile)

def profile_detail(request, username, public_profile_field=None,
                   template_name='profiles/profile_detail.html',
                   extra_context=None):
    """
    Detail view of a user's profile.

    If no profile model has been specified in the
    ``AUTH_PROFILE_MODULE`` setting,
    ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
    raised.

    If the user has not yet created a profile, ``Http404`` will be
    raised.

    **Required arguments:**

    ``username``
        The username of the user whose profile is being displayed.

    **Optional arguments:**

    ``extra_context``
        A dictionary of variables to add to the template context. Any
        callable object in this dictionary will be called to produce
        the end result which appears in the context.

    ``public_profile_field``
        The name of a ``BooleanField`` on the profile model; if the
        value of that field on the user's profile is ``False``, the
        ``profile`` variable in the template will be ``None``. Use
        this feature to allow users to mark their profiles as not
        being publicly viewable.

        If this argument is not specified, it will be assumed that all
        users' profiles are publicly viewable.

    ``template_name``
        The name of the template to use for displaying the profile. If
        not specified, this will default to
        :template:`profiles/profile_detail.html`.

    **Context:**

    ``profile``
        The user's profile, or ``None`` if the user's profile is not
        publicly viewable (see the description of
        ``public_profile_field`` above).

    **Template:**

    ``template_name`` keyword argument or
    :template:`profiles/profile_detail.html`.

    """
    user = get_object_or_404(User, username=username)
    try:
        profile_obj = user.get_profile()
    except ObjectDoesNotExist:
        return HttpResponseRedirect(reverse('create_profile'))

    #profile_obj = user.get_profile()

    if public_profile_field is not None and \
       not getattr(profile_obj, public_profile_field):
        profile_obj = None

    if extra_context is None:
        extra_context = {}
    context = RequestContext(request)
    for key, value in extra_context.items():
        context[key] = callable(value) and value() or value

    return render_to_response(template_name,
                              { 'profile': profile_obj },
                              context_instance=context)

def profile_list(request, public_profile_field=None,
                 template_name='profiles/profile_list.html', **kwargs):
    """
    A list of user profiles.

    If no profile model has been specified in the
    ``AUTH_PROFILE_MODULE`` setting,
    ``django.contrib.auth.models.SiteProfileNotAvailable`` will be
    raised.

    **Optional arguments:**

    ``public_profile_field``
        The name of a ``BooleanField`` on the profile model; if the
        value of that field on a user's profile is ``False``, that
        profile will be excluded from the list. Use this feature to
        allow users to mark their profiles as not being publicly
        viewable.

        If this argument is not specified, it will be assumed that all
        users' profiles are publicly viewable.

    ``template_name``
        The name of the template to use for displaying the profiles. If
        not specified, this will default to
        :template:`profiles/profile_list.html`.

    Additionally, all arguments accepted by the
    :view:`django.views.generic.list_detail.object_list` generic view
    will be accepted here, and applied in the same fashion, with one
    exception: ``queryset`` will always be the ``QuerySet`` of the
    model specified by the ``AUTH_PROFILE_MODULE`` setting, optionally
    filtered to remove non-publicly-viewable proiles.

    **Context:**

    Same as the :view:`django.views.generic.list_detail.object_list`
    generic view.

    **Template:**

    ``template_name`` keyword argument or
    :template:`profiles/profile_list.html`.

    """
    profile_model = utils.get_profile_model()
    queryset = profile_model._default_manager.all()
    if public_profile_field is not None:
        queryset = queryset.filter(**{ public_profile_field: True })
    kwargs['queryset'] = queryset
    return object_list(request, template_name=template_name, **kwargs)
<|endoftext|>