<commit_before>#-- encoding: utf-8 --
#
# This file is part of I4P.
#
# I4P is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# I4P is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero Public License for more details.
#
# You should have received a copy of the GNU Affero Public License
# along with I4P.  If not, see <http://www.gnu.org/licenses/>.
#
import StringIO
from urllib2 import urlopen
import urlparse
import re

from django.conf import settings
from django.core.files import File
from django.utils import simplejson

from easy_thumbnails.files import get_thumbnailer
import oauth2
from social_auth.backends.facebook import FacebookBackend
from social_auth.backends.twitter import TwitterBackend
from social_auth.backends.google import GoogleOAuth2Backend
from social_auth.backends.contrib.linkedin import LinkedinBackend

from apps.i4p_base.models import Location, I4P_COUNTRIES
from .utils import fix_username

class DataAdapter(object):
    """
    Mixin class hosting methods useful to different social data adapters.
    """

    def __init__(self, profile, response):
        self.profile = profile
        self.response = response

    def fetch_picture(self):
        """
        Get profile picture from a URL

        easy_thumbnails requires a django File object which itself needs a
        file-like object so we do a little dance with StringIO to make it work.
        """
        photo = urlopen(self.picture_url)
        photo_io = StringIO.StringIO()
        photo_io.write(photo.read())
        thumbnailer = get_thumbnailer(File(photo_io),
                relative_name=("%s%s-picture" % (settings.USERENA_MUGSHOT_PATH,
                                                 self.profile.user.username)))
        thumb = thumbnailer.generate_thumbnail({'size': (200, 200)})
        self.profile.mugshot = thumb

    def set_twitter_url(self, screen_name):
        """
        Return twitter URL from screen name
        """
        twitter_url = 'http://twitter.com/' + screen_name
        self.profile.twitter = twitter_url

    def make_username_from(self, first_name, last_name):
        """
        Set a username from profile first name and last name
        """
        default_username = self.profile.user.username
        fancy_username = first_name.capitalize() + last_name.capitalize()
        if default_username.startswith(fancy_username.lower()):
            # If default username already starts with a lowercase version of the
            # username based on first name and last name, we replace it by its
            # camel case counterpart. Example: if Google email is
            # john.smith@gmail.com, default username could be johnsmith2 if
            # there's already a JohnSmith, then we want the new fancy username
            # to be JohnSmith2
            self.profile.user.username = default_username.replace(
                        fancy_username.lower(), fancy_username)
        else:
            self.profile.user.username = fix_username(fancy_username)

class FacebookDataAdapter(DataAdapter):
    """
    Populates user profile attributes using data fetched from Facebook.

    Tries to populate these fields:
     - first_name
     - last_name
     - email
     - country
     - location
     - website
     - facebook (profile URL)
     - mugshot (profile picture)
    """

    def fetch_profile_data(self):
        """
        Get profile data from authentication response and assign it to user
        profile
        """
        self.profile.user.first_name = self.response.get('first_name', '')
        self.profile.user.last_name = self.response.get('last_name', '')
        self.make_username_from(self.profile.user.first_name,
                                self.profile.user.last_name)

        self.profile.website = self.response.get('website', '')
        self.profile.facebook = self.response.get('link', '')
        self.fetch_country()
        self.fetch_picture()

    @property
    def picture_url(self):
        template = "http://graph.facebook.com/%s/picture?type=large"
        return template % self.response['id']

    def fetch_country(self):
        try:
            location_name = self.response['location']['name']
            city, country = [s.strip() for s in location_name.split(",")]
            self.profile.address = city
            for country_code, i18n_proxy in I4P_COUNTRIES:
                # Need to coerce Django i18n proxy into unicode
                country_name = unicode(i18n_proxy)
                if country_name == country:
                    self.profile.country = country_code
                    break
            location_url = "http://graph.facebook.com/%s" % self.response['location']['id']
            location_request = urlopen(location_url)
            location_response = simplejson.loads(location_request.read())
            location_data = location_response['location']
            location = Location(lat=location_data['latitude'],
                                lon=location_data['longitude'],
                                address=city,
                                country=self.profile.country)
            self.profile.location = location
        except KeyError:
            # Don't propagate errors caused by missing data
            pass

class TwitterDataAdapter(DataAdapter):
    """
    Populates user profile attributes using data fetched from Twitter.

    Tries to populate these fields:
     - first_name
     - last_name
     - website
     - location
     - twitter (twitter profile URL)
     - mugshot (profile picture)

    Email address is NOT available via Twitter API.
    """

    def make_username(self):
        """
        Do our best to build a username that looks like a typical i4p username
        using the 'name' field returned by Twitter.
        """
        name = self.response.get('name', '').strip()
        space_regexp = re.compile("\s")
        if space_regexp.search(name):
            # If name looks like first name and last name
            # try to make an i4p-style username
            name_parts = space_regexp.split(name)
            self.profile.user.first_name = name_parts[0]
            self.profile.user.last_name = name_parts[-1]
            self.make_username_from(
                    self.profile.user.first_name,
                    self.profile.user.last_name)
        elif name:
            # Otherwise just make a username from the name
            self.profile.user.username = fix_username(name)
        # If there's no name, username default to twitter username

    def fetch_profile_data(self):
        """
        Get profile data from authentication response and assign it to user
        profile

        Twitter API doesn't currently give access to location or email address
        """
        self.set_twitter_url(self.response.get('screen_name'))
        self.fetch_picture()
        self.profile.website = self.response.get('url', '')
        location = Location(address=self.response.get('location'))
        self.profile.location = location
        self.make_username()

    @property
    def picture_url(self):
        template = "https://api.twitter.com/1/users/profile_image?screen_name=%s&size=bigger"
        return template % self.response['screen_name']

class GoogleDataAdapter(DataAdapter):
    """
    Populates user profile attributes using data fetched from Twitter.

    Tries to populate these fields:
     - first_name
     - last_name
     - email
     - mugshot (profile picture)
     - gender
    """

    def __init__(self, profile, response):
        DataAdapter.__init__(self, profile, response)
        token = self.response['access_token']
        profile_url = 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=' + token
        profile_json = urlopen(profile_url).read()
        self.user_info = simplejson.loads(profile_json)

    def fetch_profile_data(self):
        self.profile.user.first_name = self.user_info.get('given_name', '')
        self.profile.user.last_name = self.user_info.get('family_name', '')
        self.make_username_from(self.profile.user.first_name,
                                self.profile.user.last_name)
        if self.user_info.get('gender') == 'male':
            self.profile.gender = 'M'
        elif self.user_info.get('gender') == 'female':
            self.profile.gender = 'F'
        self.fetch_picture()

    @property
    def picture_url(self):
        return self.user_info['picture']

class LinkedinDataAdapter(DataAdapter):
    """
    Populates user profile attributes using data fetched from Twitter.

    Tries to populate these fields:
     - first_name
     - last_name
     - country
     - location
     - mugshot (profile picture)
     - website (first resource URLs)
     - twitter (first twitter account registered with LinkedIn)
     - linked (profile URL)

    Email address is NOT available via LinkedIn API.
    """

    def __init__(self, profile, response):
        DataAdapter.__init__(self, profile, response)

        # OAuth dance documented at
        # https://developer.linkedin.com/documents/quick-start-guide
        consumer = oauth2.Consumer(settings.LINKEDIN_CONSUMER_KEY,
                settings.LINKEDIN_CONSUMER_SECRET)
        access_token = dict(urlparse.parse_qsl(self.response['access_token']))
        token = oauth2.Token(
                    key=access_token['oauth_token'],
                    secret=access_token['oauth_token_secret'])
        client = oauth2.Client(consumer, token)

        # Simple profile call. Field selectors reference:
        # https://developer.linkedin.com/documents/profile-fields
        base_url = "http://api.linkedin.com/v1/people/~"
        fields = "(public-profile-url,picture-url,member-url-resources:(url),"
        fields += "twitter-accounts,location:(name,country:(code)))"
        url = ":".join([base_url, fields])
        _, content = client.request(url, 'GET',
                                       headers={"x-li-format": 'json'})
        self.user_info = simplejson.loads(content)

    def fetch_profile_data(self):
        """
        Fetch profile data from LinkedIn
        """
        country_code = self.user_info['location']['country']['code'].upper()
        self.profile.country = country_code

        location_name = self.user_info['location']['name']
        location = Location(address=location_name, country=country_code)
        self.profile.location = location

        self.profile.linkedin = self.user_info['publicProfileUrl']

        # Pick first URL resource as user's website
        websites = self.user_info['memberUrlResources']
        if websites['_total'] > 0:
            self.profile.website = websites['values'][0]['url']

        # Try to populate twitter account
        twitters = self.user_info['twitterAccounts']
        if twitters['_total'] > 0:
            screen_name = twitters['values'][0]['providerAccountName']
            self.set_twitter_url(screen_name)

        self.fetch_picture()

    @property
    def picture_url(self):
        return self.user_info['pictureUrl']

DATA_ADAPTERS = {
    FacebookBackend: FacebookDataAdapter,
    TwitterBackend: TwitterDataAdapter,
    GoogleOAuth2Backend: GoogleDataAdapter,
    LinkedinBackend: LinkedinDataAdapter,
}

def fetch_profile_data(backend, profile, response):
    """
    Get profile data from response returned by social backend and use if to
    populate user profile.
    """
    if backend in DATA_ADAPTERS:
        adapter_class = DATA_ADAPTERS[backend]
        try:
            adapter = adapter_class(profile, response)
            adapter.fetch_profile_data()
        except Exception:
            # Errors while fetching profile data shouldn't be fatal.
            # Automatically populating user's profile is a nice thing to do, but
            # we don't want it to stop the registration process if it doesn't
            # work.
            pass

        profile.save()
<commit_msg>Import order<commit_after>#-- encoding: utf-8 --
#
# This file is part of I4P.
#
# I4P is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# I4P is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero Public License for more details.
#
# You should have received a copy of the GNU Affero Public License
# along with I4P.  If not, see <http://www.gnu.org/licenses/>.
#
import re
import StringIO
from urllib2 import urlopen
import urlparse

from django.conf import settings
from django.core.files import File
from django.utils import simplejson

from easy_thumbnails.files import get_thumbnailer
import oauth2
from social_auth.backends.facebook import FacebookBackend
from social_auth.backends.twitter import TwitterBackend
from social_auth.backends.google import GoogleOAuth2Backend
from social_auth.backends.contrib.linkedin import LinkedinBackend

from apps.i4p_base.models import Location, I4P_COUNTRIES
from .utils import fix_username

class DataAdapter(object):
    """
    Mixin class hosting methods useful to different social data adapters.
    """

    def __init__(self, profile, response):
        self.profile = profile
        self.response = response

    def fetch_picture(self):
        """
        Get profile picture from a URL

        easy_thumbnails requires a django File object which itself needs a
        file-like object so we do a little dance with StringIO to make it work.
        """
        photo = urlopen(self.picture_url)
        photo_io = StringIO.StringIO()
        photo_io.write(photo.read())
        thumbnailer = get_thumbnailer(File(photo_io),
                relative_name=("%s%s-picture" % (settings.USERENA_MUGSHOT_PATH,
                                                 self.profile.user.username)))
        thumb = thumbnailer.generate_thumbnail({'size': (200, 200)})
        self.profile.mugshot = thumb

    def set_twitter_url(self, screen_name):
        """
        Return twitter URL from screen name
        """
        twitter_url = 'http://twitter.com/' + screen_name
        self.profile.twitter = twitter_url

    def make_username_from(self, first_name, last_name):
        """
        Set a username from profile first name and last name
        """
        default_username = self.profile.user.username
        fancy_username = first_name.capitalize() + last_name.capitalize()
        if default_username.startswith(fancy_username.lower()):
            # If default username already starts with a lowercase version of the
            # username based on first name and last name, we replace it by its
            # camel case counterpart. Example: if Google email is
            # john.smith@gmail.com, default username could be johnsmith2 if
            # there's already a JohnSmith, then we want the new fancy username
            # to be JohnSmith2
            self.profile.user.username = default_username.replace(
                        fancy_username.lower(), fancy_username)
        else:
            self.profile.user.username = fix_username(fancy_username)

class FacebookDataAdapter(DataAdapter):
    """
    Populates user profile attributes using data fetched from Facebook.

    Tries to populate these fields:
     - first_name
     - last_name
     - email
     - country
     - location
     - website
     - facebook (profile URL)
     - mugshot (profile picture)
    """

    def fetch_profile_data(self):
        """
        Get profile data from authentication response and assign it to user
        profile
        """
        self.profile.user.first_name = self.response.get('first_name', '')
        self.profile.user.last_name = self.response.get('last_name', '')
        self.make_username_from(self.profile.user.first_name,
                                self.profile.user.last_name)

        self.profile.website = self.response.get('website', '')
        self.profile.facebook = self.response.get('link', '')
        self.fetch_country()
        self.fetch_picture()

    @property
    def picture_url(self):
        template = "http://graph.facebook.com/%s/picture?type=large"
        return template % self.response['id']

    def fetch_country(self):
        try:
            location_name = self.response['location']['name']
            city, country = [s.strip() for s in location_name.split(",")]
            self.profile.address = city
            for country_code, i18n_proxy in I4P_COUNTRIES:
                # Need to coerce Django i18n proxy into unicode
                country_name = unicode(i18n_proxy)
                if country_name == country:
                    self.profile.country = country_code
                    break
            location_url = "http://graph.facebook.com/%s" % self.response['location']['id']
            location_request = urlopen(location_url)
            location_response = simplejson.loads(location_request.read())
            location_data = location_response['location']
            location = Location(lat=location_data['latitude'],
                                lon=location_data['longitude'],
                                address=city,
                                country=self.profile.country)
            self.profile.location = location
        except KeyError:
            # Don't propagate errors caused by missing data
            pass

class TwitterDataAdapter(DataAdapter):
    """
    Populates user profile attributes using data fetched from Twitter.

    Tries to populate these fields:
     - first_name
     - last_name
     - website
     - location
     - twitter (twitter profile URL)
     - mugshot (profile picture)

    Email address is NOT available via Twitter API.
    """

    def make_username(self):
        """
        Do our best to build a username that looks like a typical i4p username
        using the 'name' field returned by Twitter.
        """
        name = self.response.get('name', '').strip()
        space_regexp = re.compile("\s")
        if space_regexp.search(name):
            # If name looks like first name and last name
            # try to make an i4p-style username
            name_parts = space_regexp.split(name)
            self.profile.user.first_name = name_parts[0]
            self.profile.user.last_name = name_parts[-1]
            self.make_username_from(
                    self.profile.user.first_name,
                    self.profile.user.last_name)
        elif name:
            # Otherwise just make a username from the name
            self.profile.user.username = fix_username(name)
        # If there's no name, username default to twitter username

    def fetch_profile_data(self):
        """
        Get profile data from authentication response and assign it to user
        profile

        Twitter API doesn't currently give access to location or email address
        """
        self.set_twitter_url(self.response.get('screen_name'))
        self.fetch_picture()
        self.profile.website = self.response.get('url', '')
        location = Location(address=self.response.get('location'))
        self.profile.location = location
        self.make_username()

    @property
    def picture_url(self):
        template = "https://api.twitter.com/1/users/profile_image?screen_name=%s&size=bigger"
        return template % self.response['screen_name']

class GoogleDataAdapter(DataAdapter):
    """
    Populates user profile attributes using data fetched from Twitter.

    Tries to populate these fields:
     - first_name
     - last_name
     - email
     - mugshot (profile picture)
     - gender
    """

    def __init__(self, profile, response):
        DataAdapter.__init__(self, profile, response)
        token = self.response['access_token']
        profile_url = 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=' + token
        profile_json = urlopen(profile_url).read()
        self.user_info = simplejson.loads(profile_json)

    def fetch_profile_data(self):
        self.profile.user.first_name = self.user_info.get('given_name', '')
        self.profile.user.last_name = self.user_info.get('family_name', '')
        self.make_username_from(self.profile.user.first_name,
                                self.profile.user.last_name)
        if self.user_info.get('gender') == 'male':
            self.profile.gender = 'M'
        elif self.user_info.get('gender') == 'female':
            self.profile.gender = 'F'
        self.fetch_picture()

    @property
    def picture_url(self):
        return self.user_info['picture']

class LinkedinDataAdapter(DataAdapter):
    """
    Populates user profile attributes using data fetched from Twitter.

    Tries to populate these fields:
     - first_name
     - last_name
     - country
     - location
     - mugshot (profile picture)
     - website (first resource URLs)
     - twitter (first twitter account registered with LinkedIn)
     - linked (profile URL)

    Email address is NOT available via LinkedIn API.
    """

    def __init__(self, profile, response):
        DataAdapter.__init__(self, profile, response)

        # OAuth dance documented at
        # https://developer.linkedin.com/documents/quick-start-guide
        consumer = oauth2.Consumer(settings.LINKEDIN_CONSUMER_KEY,
                settings.LINKEDIN_CONSUMER_SECRET)
        access_token = dict(urlparse.parse_qsl(self.response['access_token']))
        token = oauth2.Token(
                    key=access_token['oauth_token'],
                    secret=access_token['oauth_token_secret'])
        client = oauth2.Client(consumer, token)

        # Simple profile call. Field selectors reference:
        # https://developer.linkedin.com/documents/profile-fields
        base_url = "http://api.linkedin.com/v1/people/~"
        fields = "(public-profile-url,picture-url,member-url-resources:(url),"
        fields += "twitter-accounts,location:(name,country:(code)))"
        url = ":".join([base_url, fields])
        _, content = client.request(url, 'GET',
                                       headers={"x-li-format": 'json'})
        self.user_info = simplejson.loads(content)

    def fetch_profile_data(self):
        """
        Fetch profile data from LinkedIn
        """
        country_code = self.user_info['location']['country']['code'].upper()
        self.profile.country = country_code

        location_name = self.user_info['location']['name']
        location = Location(address=location_name, country=country_code)
        self.profile.location = location

        self.profile.linkedin = self.user_info['publicProfileUrl']

        # Pick first URL resource as user's website
        websites = self.user_info['memberUrlResources']
        if websites['_total'] > 0:
            self.profile.website = websites['values'][0]['url']

        # Try to populate twitter account
        twitters = self.user_info['twitterAccounts']
        if twitters['_total'] > 0:
            screen_name = twitters['values'][0]['providerAccountName']
            self.set_twitter_url(screen_name)

        self.fetch_picture()

    @property
    def picture_url(self):
        return self.user_info['pictureUrl']

DATA_ADAPTERS = {
    FacebookBackend: FacebookDataAdapter,
    TwitterBackend: TwitterDataAdapter,
    GoogleOAuth2Backend: GoogleDataAdapter,
    LinkedinBackend: LinkedinDataAdapter,
}

def fetch_profile_data(backend, profile, response):
    """
    Get profile data from response returned by social backend and use if to
    populate user profile.
    """
    if backend in DATA_ADAPTERS:
        adapter_class = DATA_ADAPTERS[backend]
        try:
            adapter = adapter_class(profile, response)
            adapter.fetch_profile_data()
        except Exception:
            # Errors while fetching profile data shouldn't be fatal.
            # Automatically populating user's profile is a nice thing to do, but
            # we don't want it to stop the registration process if it doesn't
            # work.
            pass

        profile.save()
<|endoftext|>


<commit_before>from xmpp import *
from spade import *
from spade.ACLMessage import *

class rosterPlugIn(PlugIn):
        #NS='jabber:x:fipa'
        NS=''
	def plugin(self, server):
		server.Dispatcher.RegisterNamespaceHandler(NS_ROSTER,self.rosterHandler)
		self.DEBUG("Roster manager loaded", "warn")

        def rosterHandler(self, session, stanza):
                print "####################################"
		print "ROSTER HANDLER CALLED"
                print "####################################"

<commit_msg>packaging<commit_after>from xmpp import *
from spade import *
from spade.ACLMessage import *

class rosterPlugIn(PlugIn):
        #NS='jabber:x:fipa'
        NS=''
	def plugin(self, server):
		server.Dispatcher.RegisterNamespaceHandler(NS_ROSTER,self.rosterHandler)
		server.Dispatcher.RegisterNamespaceHandler(u'jabber:iq:roster',self.rosterHandler)
		self.DEBUG("Roster manager loaded", "warn")

        def rosterHandler(self, session, stanza):
                print "####################################"
		print "ROSTER HANDLER CALLED"
                print "####################################"

<|endoftext|>


<commit_before>from .DT import *
from .AdADT import *
from .BaGQuasiRDT import *
<commit_msg>Update __init__.py<commit_after>from .AdADT import *
from .BaGQuasiRDT import *
<|endoftext|>


<commit_before>import logging
from os import remove
from django_dynamic_fixture import G
from django.test import TestCase
from apps.offline.models import IMAGE_FOLDER, Issue

class OfflineTest(TestCase):

    def setUp(self):
        self.logger = logging.getLogger(__name__)
        self.issue = G(Issue, issue=IMAGE_FOLDER + '/offline-test-pdf.pdf')

    def testThumbnailExists(self):
        self.assertTrue(self.issue.thumbnail_exists)
        remove(self.issue.thumbnail)
<commit_msg>Moved the thumbnail removal to the more appropriate tearDown method.<commit_after>import logging
from os import remove
from django_dynamic_fixture import G
from django.test import TestCase
from apps.offline.models import IMAGE_FOLDER, Issue

class OfflineTest(TestCase):

    def setUp(self):
        self.logger = logging.getLogger(__name__)
        self.issue = G(Issue, issue=IMAGE_FOLDER + '/offline-test-pdf.pdf')

    def testThumbnailExists(self):
        self.assertTrue(self.issue.thumbnail_exists)

    def tearDown(self):
        remove(self.issue.thumbnail)
<|endoftext|>


<commit_before>from random import randrange
import simplejson
import urllib

from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.core.cache import cache
from django.conf import settings
from django.db.models import Q, Count
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.template.loader import render_to_string

from homepage.models import Dpotw, Gotw

from package.forms import PackageForm, PackageExampleForm
from package.models import Category, Package, PackageExample

def repos_for_js():
    repos = {}
    for repo in []: #XXX Repo.objects.all():
        repos[repo.url] = repo.id
    return simplejson.dumps(repos)

@login_required
def add_package(request, template_name="package/package_form.html"):

    new_package = Package()
    form = PackageForm(request.POST or None, instance=new_package)

    if form.is_valid():
        new_package = form.save()
        new_package.created_by = request.user
        new_package.last_modified_by = request.user
        new_package.save()
        new_package.fetch_metadata()
        return HttpResponseRedirect(reverse("package", kwargs={"slug":new_package.slug}))

    return render_to_response(template_name, {
        "form": form,
        "repos": repos_for_js(),
        "action": "add",
        },
        context_instance=RequestContext(request))

@login_required
def edit_package(request, slug, template_name="package/package_form.html"):

    package = get_object_or_404(Package, slug=slug)
    form = PackageForm(request.POST or None, instance=package)

    if form.is_valid():
        modified_package = form.save()
        modified_package.last_modified_by = request.user
        modified_package.save()

        return HttpResponseRedirect(reverse("package", kwargs={"slug": modified_package.slug}))

    return render_to_response(template_name, {
        "form": form,
        "package": package,
        "repos": repos_for_js(),
        "action": "edit",
        },
        context_instance=RequestContext(request))

@login_required
def update_package(request, slug):

    package = get_object_or_404(Package, slug=slug)
    package.fetch_metadata()

    return HttpResponseRedirect(reverse("package", kwargs={"slug": package.slug}))

@login_required
def add_example(request, slug, template_name="package/add_example.html"):

    package = get_object_or_404(Package, slug=slug)
    new_package_example = PackageExample()
    form = PackageExampleForm(request.POST or None, instance=new_package_example)

    if form.is_valid():
        package_example = PackageExample(package=package,
                title=request.POST["title"],
                url=request.POST["url"])
        package_example.save()
        return HttpResponseRedirect(reverse("package", kwargs={"slug":package_example.package.slug}))

    return render_to_response(template_name, {
        "form": form,
        "package":package
        },
        context_instance=RequestContext(request))

@login_required
def edit_example(request, slug, id, template_name="package/edit_example.html"):

    package_example = get_object_or_404(PackageExample, id=id)
    form = PackageExampleForm(request.POST or None, instance=package_example)

    if form.is_valid():
        form.save()
        return HttpResponseRedirect(reverse("package", kwargs={"slug": package_example.package.slug}))

    return render_to_response(template_name, {
        "form": form,
        "package":package_example.package
        },
        context_instance=RequestContext(request))

def package_autocomplete(request):
    """
    Provides Package matching based on matches of the beginning
    """
    titles = []
    q = request.GET.get("q", "")
    if q:
        titles = (x.title for x in Package.objects.filter(title__istartswith=q))

    response = HttpResponse("\n".join(titles))

    setattr(response, "djangologging.suppress_output", True)
    return response

def category(request, slug, template_name="package/category.html"):
    category = get_object_or_404(Category, slug=slug)
    packages = category.package_set.annotate(usage_count=Count("usage")).order_by("-pypi_downloads", "-repo_watchers", "title")
    return render_to_response(template_name, {
        "category": category,
        "packages": packages,
        },
        context_instance=RequestContext(request)
    )

def ajax_package_list(request, template_name="package/ajax_package_list.html"):
    q = request.GET.get("q","")
    packages = []
    if q:
        django_dash = "django-%s" % q
        django_space = "django %s" % q
        packages = Package.objects.filter(
                        Q(title__istartswith=q) |
                        Q(title__istartswith=django_dash) |
                        Q(title__istartswith=django_space)
                    )
    return render_to_response(template_name, {
        "packages": packages
        },
        context_instance=RequestContext(request)
    )

def usage(request, slug, action):
    success = False
    # Check if the user is authenticated, redirecting them to the login page if
    # they're not.
    if not request.user.is_authenticated():
        url = settings.LOGIN_URL + '?next=%s' % reverse('usage', args=(slug, action))
        referer = request.META.get('HTTP_REFERER')
        if referer:
            url += urllib.quote_plus('?next=/%s' % referer.split('/', 3)[-1])

        if request.is_ajax():
            response = {}
            response['success'] = success
            response['redirect'] = url
            return HttpResponse(simplejson.dumps(response))
        return HttpResponseRedirect(url)

    package = get_object_or_404(Package, slug=slug)

    # Update the current user's usage of the given package as specified by the
    # request.
    if package.usage.filter(username=request.user.username):
        if action.lower() == 'remove':
            package.usage.remove(request.user)
            success = True
            template_name = 'package/add_usage_button.html'
            change = -1
    else:
        if action.lower() == 'add':
            package.usage.add(request.user)
            success = True
            template_name = 'package/remove_usage_button.html'
            change = 1

    # Invalidate the cache of this users's used_packages_list.
    if success:
        cache_key = "sitewide_used_packages_list_%s" % request.user.pk
        cache.delete(cache_key)

    # Return an ajax-appropriate response if necessary
    if request.is_ajax():
        response = {'success': success}
        if success:
            response['change'] = change
            response['body'] = render_to_string(
                template_name,
                {"package": package},
            )
        return HttpResponse(simplejson.dumps(response))

    # Intelligently determine the URL to redirect the user to based on the
    # available information.
    next = request.GET.get('next') or request.META.get("HTTP_REFERER") or reverse("package", kwargs={"slug": package.slug})
    return HttpResponseRedirect(next)

def packaginate(request):
    """ Special project method - DO NOT TOUCH!!! """

    packages = Package.objects.all()
    package = packages[randrange(0, packages.count())]
    response = dict(
            title = package.title,
            url = package.get_absolute_url(),
            description=package.repo_description
        )
    return HttpResponse(simplejson.dumps(response))

def package_list(request, template_name="package/package_list.html"):

    categories = []
    for category in Category.objects.annotate(package_count=Count("package")):
        element = {
            "title":category.title,
            "description":category.description,
            "count": category.package_count,
            "slug": category.slug,
            "title_plural": category.title_plural,
            "show_pypi": category.show_pypi,
            "packages": category.package_set.annotate(usage_count=Count("usage")).order_by("-pypi_downloads", "-repo_watchers", "title")[:9]
        }
        categories.append(element)

    return render_to_response(
        template_name, {
            "categories": categories,
            "dpotw": Dpotw.objects.get_current(),
            "gotw": Gotw.objects.get_current(),
        }, context_instance = RequestContext(request)
    )
<commit_msg>Fix repos_for_js<commit_after>from random import randrange
import simplejson
import urllib

from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.core.cache import cache
from django.conf import settings
from django.db.models import Q, Count
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.template.loader import render_to_string

from homepage.models import Dpotw, Gotw

from package.forms import PackageForm, PackageExampleForm
from package.models import Category, Package, PackageExample
from package.repos import supported_repos, get_repo

def repos_for_js():
    repos = {}
    for repo_id in supported_repos():
        repos[get_repo(repo_id).url] = repo_id
    return simplejson.dumps(repos)

@login_required
def add_package(request, template_name="package/package_form.html"):

    new_package = Package()
    form = PackageForm(request.POST or None, instance=new_package)

    if form.is_valid():
        new_package = form.save()
        new_package.created_by = request.user
        new_package.last_modified_by = request.user
        new_package.save()
        new_package.fetch_metadata()
        return HttpResponseRedirect(reverse("package", kwargs={"slug":new_package.slug}))

    return render_to_response(template_name, {
        "form": form,
        "repos": repos_for_js(),
        "action": "add",
        },
        context_instance=RequestContext(request))

@login_required
def edit_package(request, slug, template_name="package/package_form.html"):

    package = get_object_or_404(Package, slug=slug)
    form = PackageForm(request.POST or None, instance=package)

    if form.is_valid():
        modified_package = form.save()
        modified_package.last_modified_by = request.user
        modified_package.save()

        return HttpResponseRedirect(reverse("package", kwargs={"slug": modified_package.slug}))

    return render_to_response(template_name, {
        "form": form,
        "package": package,
        "repos": repos_for_js(),
        "action": "edit",
        },
        context_instance=RequestContext(request))

@login_required
def update_package(request, slug):

    package = get_object_or_404(Package, slug=slug)
    package.fetch_metadata()

    return HttpResponseRedirect(reverse("package", kwargs={"slug": package.slug}))

@login_required
def add_example(request, slug, template_name="package/add_example.html"):

    package = get_object_or_404(Package, slug=slug)
    new_package_example = PackageExample()
    form = PackageExampleForm(request.POST or None, instance=new_package_example)

    if form.is_valid():
        package_example = PackageExample(package=package,
                title=request.POST["title"],
                url=request.POST["url"])
        package_example.save()
        return HttpResponseRedirect(reverse("package", kwargs={"slug":package_example.package.slug}))

    return render_to_response(template_name, {
        "form": form,
        "package":package
        },
        context_instance=RequestContext(request))

@login_required
def edit_example(request, slug, id, template_name="package/edit_example.html"):

    package_example = get_object_or_404(PackageExample, id=id)
    form = PackageExampleForm(request.POST or None, instance=package_example)

    if form.is_valid():
        form.save()
        return HttpResponseRedirect(reverse("package", kwargs={"slug": package_example.package.slug}))

    return render_to_response(template_name, {
        "form": form,
        "package":package_example.package
        },
        context_instance=RequestContext(request))

def package_autocomplete(request):
    """
    Provides Package matching based on matches of the beginning
    """
    titles = []
    q = request.GET.get("q", "")
    if q:
        titles = (x.title for x in Package.objects.filter(title__istartswith=q))

    response = HttpResponse("\n".join(titles))

    setattr(response, "djangologging.suppress_output", True)
    return response

def category(request, slug, template_name="package/category.html"):
    category = get_object_or_404(Category, slug=slug)
    packages = category.package_set.annotate(usage_count=Count("usage")).order_by("-pypi_downloads", "-repo_watchers", "title")
    return render_to_response(template_name, {
        "category": category,
        "packages": packages,
        },
        context_instance=RequestContext(request)
    )

def ajax_package_list(request, template_name="package/ajax_package_list.html"):
    q = request.GET.get("q","")
    packages = []
    if q:
        django_dash = "django-%s" % q
        django_space = "django %s" % q
        packages = Package.objects.filter(
                        Q(title__istartswith=q) |
                        Q(title__istartswith=django_dash) |
                        Q(title__istartswith=django_space)
                    )
    return render_to_response(template_name, {
        "packages": packages
        },
        context_instance=RequestContext(request)
    )

def usage(request, slug, action):
    success = False
    # Check if the user is authenticated, redirecting them to the login page if
    # they're not.
    if not request.user.is_authenticated():
        url = settings.LOGIN_URL + '?next=%s' % reverse('usage', args=(slug, action))
        referer = request.META.get('HTTP_REFERER')
        if referer:
            url += urllib.quote_plus('?next=/%s' % referer.split('/', 3)[-1])

        if request.is_ajax():
            response = {}
            response['success'] = success
            response['redirect'] = url
            return HttpResponse(simplejson.dumps(response))
        return HttpResponseRedirect(url)

    package = get_object_or_404(Package, slug=slug)

    # Update the current user's usage of the given package as specified by the
    # request.
    if package.usage.filter(username=request.user.username):
        if action.lower() == 'remove':
            package.usage.remove(request.user)
            success = True
            template_name = 'package/add_usage_button.html'
            change = -1
    else:
        if action.lower() == 'add':
            package.usage.add(request.user)
            success = True
            template_name = 'package/remove_usage_button.html'
            change = 1

    # Invalidate the cache of this users's used_packages_list.
    if success:
        cache_key = "sitewide_used_packages_list_%s" % request.user.pk
        cache.delete(cache_key)

    # Return an ajax-appropriate response if necessary
    if request.is_ajax():
        response = {'success': success}
        if success:
            response['change'] = change
            response['body'] = render_to_string(
                template_name,
                {"package": package},
            )
        return HttpResponse(simplejson.dumps(response))

    # Intelligently determine the URL to redirect the user to based on the
    # available information.
    next = request.GET.get('next') or request.META.get("HTTP_REFERER") or reverse("package", kwargs={"slug": package.slug})
    return HttpResponseRedirect(next)

def packaginate(request):
    """ Special project method - DO NOT TOUCH!!! """

    packages = Package.objects.all()
    package = packages[randrange(0, packages.count())]
    response = dict(
            title = package.title,
            url = package.get_absolute_url(),
            description=package.repo_description
        )
    return HttpResponse(simplejson.dumps(response))

def package_list(request, template_name="package/package_list.html"):

    categories = []
    for category in Category.objects.annotate(package_count=Count("package")):
        element = {
            "title":category.title,
            "description":category.description,
            "count": category.package_count,
            "slug": category.slug,
            "title_plural": category.title_plural,
            "show_pypi": category.show_pypi,
            "packages": category.package_set.annotate(usage_count=Count("usage")).order_by("-pypi_downloads", "-repo_watchers", "title")[:9]
        }
        categories.append(element)

    return render_to_response(
        template_name, {
            "categories": categories,
            "dpotw": Dpotw.objects.get_current(),
            "gotw": Gotw.objects.get_current(),
        }, context_instance = RequestContext(request)
    )
<|endoftext|>


<commit_before>from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.conf import settings
admin.autodiscover()

from front.views import *
from front.views import views as front_views

from django.views.decorators.csrf import csrf_exempt

if not settings.DEBUG:
    s = {'SSL': settings.ENABLE_SSL}
else:
    s = {}

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'DisSoNet.views.home', name='home'),
    url(r'^$', HomeView.as_view(), name='home'),

    url(r'^$', HomeView.as_view(), name='home'),

    url(r'^test/', front_views.test, name='test'),

    url(r'^privacy/', front_views.privacy, name='privacy'),

    url(r'^stream_debug/', front_views.stream_debug, name='stream'),

    url(r'^admin/', include(admin.site.urls)),

    url(r'^github/setup/', GitHubView.as_view(), s, name='initGithub'),

    url(r'^accounts/login/', LoginView.as_view(), s, name='login'),
    url(r'^accounts/logout/', LogoutView.as_view(), s, name='logout'),
    url(r'^accounts/view/', UserView.as_view(), s, name='user_view'),
    url(r'^accounts/register/', RegisterView.as_view(), s, name='register'),
    url(r'^accounts/reset/$', front_views.reset, s, name='reset'),
    url(r'^accounts/reset/e/(?P<email>.+)/$', front_views.reset,
        s, name='reset'),
    url(r'^accounts/reset/done/$', front_views.reset_done,
        s, name='reset_done'),
    url(r'^accounts/reset/confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
        front_views.reset_confirm, s, name='reset_confirm'),
    url(r'^accounts/reset/complete/$', front_views.reset_complete,
        name='reset_complete'),

    # urls for post(s)/
    url(r'^post/?$', PublicPosts.as_view(), name='public_posts'),
    url(r'^posts/?$', PublicPosts.as_view(), name='public_posts'),
    # urls for post(s)/<post_id>/
    url(r'^post/(?P<post_id>.+)/?$', PostResource.as_view(), name='post_resource'),
    url(r'^posts/(?P<post_id>.+)/?$', PostResource.as_view(), name='post_resource'),
    # urls for post(s)/<post_id>/comments/
    url(r'^post/(?P<post_id>.+)/comments/?$',
        csrf_exempt(PostComments.as_view()), name='post_comments'),
    url(r'^posts/(?P<post_id>.+)/comments/?$',
        csrf_exempt(PostComments.as_view()), name='post_comments'),

    url(r'^author/posts/?$', AuthorStream.as_view(), name='author_posts'),
    url(r'^author/(?P<author_id>.+)/posts/?$', VisiblePostToUser.as_view(),
        name='visibile_posts'),
    url(r'^author/(?P<author_id>.+)/?$', AuthorProfile.as_view(),
        name='author_profile'),

    url(r'^friendrequest/$', csrf_exempt(FriendRequestView.as_view()),
        name='friend_request'),

    url(r'^friends/(?P<user_id_1>.+)/(?P<user_id_2>.+)/$',
        AreFriends.as_view(), name='are_friends'),
    url(r'^friends/?', FriendsView.as_view(),
        s, name='friends_view'),

    url(r'^test_rest/(?P<id>.+)/?$', front_views.test_rest, name="test_rest"),
)
<commit_msg>.+ includes slashes, take it out<commit_after>from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.conf import settings
admin.autodiscover()

from front.views import *
from front.views import views as front_views

from django.views.decorators.csrf import csrf_exempt

if not settings.DEBUG:
    s = {'SSL': settings.ENABLE_SSL}
else:
    s = {}

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'DisSoNet.views.home', name='home'),
    url(r'^$', HomeView.as_view(), name='home'),

    url(r'^$', HomeView.as_view(), name='home'),

    url(r'^test/', front_views.test, name='test'),

    url(r'^privacy/', front_views.privacy, name='privacy'),

    url(r'^stream_debug/', front_views.stream_debug, name='stream'),

    url(r'^admin/', include(admin.site.urls)),

    url(r'^github/setup/', GitHubView.as_view(), s, name='initGithub'),

    url(r'^accounts/login/', LoginView.as_view(), s, name='login'),
    url(r'^accounts/logout/', LogoutView.as_view(), s, name='logout'),
    url(r'^accounts/view/', UserView.as_view(), s, name='user_view'),
    url(r'^accounts/register/', RegisterView.as_view(), s, name='register'),
    url(r'^accounts/reset/$', front_views.reset, s, name='reset'),
    url(r'^accounts/reset/e/(?P<email>.+)/$', front_views.reset,
        s, name='reset'),
    url(r'^accounts/reset/done/$', front_views.reset_done,
        s, name='reset_done'),
    url(r'^accounts/reset/confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
        front_views.reset_confirm, s, name='reset_confirm'),
    url(r'^accounts/reset/complete/$', front_views.reset_complete,
        name='reset_complete'),

    # urls for post(s)/
    url(r'^post/?$', PublicPosts.as_view(), name='public_posts'),
    url(r'^posts/?$', PublicPosts.as_view(), name='public_posts'),
    # urls for post(s)/<post_id>/
    url(r'^post/(?P<post_id>.+)?$', PostResource.as_view(), name='post_resource'),
    url(r'^posts/(?P<post_id>.+)?$', PostResource.as_view(), name='post_resource'),
    # urls for post(s)/<post_id>/comments/
    url(r'^post/(?P<post_id>.+)/comments/?$',
        csrf_exempt(PostComments.as_view()), name='post_comments'),
    url(r'^posts/(?P<post_id>.+)/comments/?$',
        csrf_exempt(PostComments.as_view()), name='post_comments'),

    url(r'^author/posts/?$', AuthorStream.as_view(), name='author_posts'),
    url(r'^author/(?P<author_id>.+)/posts/?$', VisiblePostToUser.as_view(),
        name='visibile_posts'),
    url(r'^author/(?P<author_id>.+)?$', AuthorProfile.as_view(),
        name='author_profile'),

    url(r'^friendrequest/$', csrf_exempt(FriendRequestView.as_view()),
        name='friend_request'),

    url(r'^friends/(?P<user_id_1>.+)/(?P<user_id_2>.+)/$',
        AreFriends.as_view(), name='are_friends'),
    url(r'^friends/?', FriendsView.as_view(),
        s, name='friends_view'),

    url(r'^test_rest/(?P<id>.+)/?$', front_views.test_rest, name="test_rest"),
)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import json
from datetime import datetime

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.mail import send_mail
from django.http import HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.translation import ugettext as _

import stripe

from apps.lan.models import Ticket, TicketType

def payment_info_static(request):
    return render(request, 'payment/info.html')

def payment_info(request, ticket_type_id):
    return render(
        request,
        'payment/info.html',
        {
            'ticket_type_id': ticket_type_id,
        })

@login_required()
def payment(request, ticket_type_id):
    stripe.api_key = settings.STRIPE_PRIVATE_KEY

    ticket_type = get_object_or_404(TicketType, pk=ticket_type_id)
    is_post = request.method == 'POST'

    if ticket_type.lan.end_date < datetime.now():
        # "The LAN is over" is already visible on the page
        if is_post:
            return JsonResponse({'error': ''})
        return redirect('lan_details', lan_id=ticket_type.lan_id)

    if ticket_type.lan.has_ticket(request.user) or request.user in ticket_type.lan.paid_attendees:
        messages.info(request, _(u'You already have a ticket for this LAN.'))
        if is_post:
            return JsonResponse({'error': ''})
        return redirect('lan_details', lan_id=ticket_type.lan_id)

    if not ticket_type.is_available():
        messages.info(request, _(u'This ticket is not yet available.'))
        if is_post:
            return JsonResponse({'error': ''})
        return redirect('lan_details', lan_id=ticket_type.lan_id)

    if ticket_type.is_sold_out():
        messages.info(request, _(u'All tickets have sold out.'))
        if is_post:
            return JsonResponse({'error': ''})
        return redirect('lan_details', lan_id=ticket_type.lan_id)

    if request.user not in ticket_type.lan.attendees:
        messages.info(request, _(u'You must attend first to buy a ticket for this LAN.'))
        if is_post:
            return JsonResponse({'error': ''})
        return redirect('lan_details', lan_id=ticket_type.lan_id)

    if request.method == 'GET':
        return render(
            request,
            'payment/checkout.html',
            {
                'ticket_type': ticket_type,
                'lan': ticket_type.lan,
            })

    if request.method == 'POST':
        description = u'{lan} {ticket} ticket for {user}'.format(ticket=ticket_type, lan=ticket_type.lan, user=request.user)
        json_data = json.loads(request.body)
        intent = None
        try:
            if 'payment_method_id' in request.body:
                # Create the PaymentIntent
                intent = stripe.PaymentIntent.create(
                    payment_method=str(json_data['payment_method_id']),
                    amount=ticket_type.price * 100,
                    currency='nok',
                    description=description,
                    confirmation_method='manual',
                    confirm=True,
                )
            elif 'payment_intent_id' in request.body:
                intent = stripe.PaymentIntent.confirm(str(json_data['payment_intent_id']))
        except stripe.error.CardError as e:
            messages.error(request, _(e.user_message))
            return JsonResponse({'error': e.user_message})

        return generate_payment_response(request, ticket_type, intent)

    return HttpResponseRedirect('/')

def generate_payment_response(request, ticket_type, intent):
    # Instruct Stripe.js to handle SCA if required
    if intent.status == 'requires_action' and intent.next_action.type == 'use_stripe_sdk':
        return JsonResponse({
            'requires_action': True,
            'payment_intent_client_secret': intent.client_secret,
        })
    elif intent.status == 'succeeded':

        ticket = Ticket()
        ticket.user = request.user
        ticket.ticket_type = ticket_type
        ticket.bought_date = datetime.now()
        ticket.save()

        lan = ticket.ticket_type.lan
        lan_link = request.build_absolute_uri(reverse('lan_details', kwargs={'lan_id': lan.id}))
        context = {
            'ticket': ticket,
            'lan': lan,
            'lan_link': lan_link,
        }
        txt_message = render_to_string('payment/email/ticket_receipt.txt', context, request).strip()
        html_message = render_to_string('payment/email/ticket_receipt.html', context, request).strip()
        send_mail(
            subject=_(u'Ticket confirmation'),
            from_email=settings.STUDLAN_FROM_MAIL,
            recipient_list=[ticket.user.email],
            message=txt_message,
            html_message=html_message,
        )

        messages.success(
            request,
            _(u'Payment complete.'),
        )

        return JsonResponse({'success': True})
    else:
        messages.error(request, _(u'Payment unsuccessful. Please contact support.'))
        return JsonResponse({'error': 'Invalid PaymentIntent status'})
<commit_msg>Refactor some payment code<commit_after># -*- coding: utf-8 -*-

import json
from datetime import datetime

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.mail import send_mail
from django.http import HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.translation import ugettext as _

import stripe

from apps.lan.models import Ticket, TicketType

def payment_info_static(request):
    return render(request, 'payment/info.html')

def payment_info(request, ticket_type_id):
    return render(
        request,
        'payment/info.html',
        {
            'ticket_type_id': ticket_type_id,
        })

@login_required()
def payment(request, ticket_type_id):
    stripe.api_key = settings.STRIPE_PRIVATE_KEY

    ticket_type = get_object_or_404(TicketType, pk=ticket_type_id)
    is_post = request.method == 'POST'

    if is_post:
        # Stripe
        error_response = JsonResponse({'error': ''})
    else:
        # User
        error_response = redirect('lan_details', lan_id=ticket_type.lan_id)

    if ticket_type.lan.end_date < datetime.now():
        # "The LAN is over" is already visible on the page
        return error_response

    if ticket_type.lan.has_ticket(request.user) or request.user in ticket_type.lan.paid_attendees:
        messages.info(request, _(u'You already have a ticket for this LAN.'))
        return error_response

    if not ticket_type.is_available():
        messages.info(request, _(u'This ticket is not yet available.'))
        return error_response

    if ticket_type.is_sold_out():
        messages.info(request, _(u'All tickets have sold out.'))
        return error_response

    if request.user not in ticket_type.lan.attendees:
        messages.info(request, _(u'You must attend first to buy a ticket for this LAN.'))
        return error_response

    if request.method == 'GET':
        return render(
            request,
            'payment/checkout.html',
            {
                'ticket_type': ticket_type,
                'lan': ticket_type.lan,
            })

    if request.method == 'POST':
        description = u'{lan} {ticket} ticket for {user}'.format(ticket=ticket_type, lan=ticket_type.lan, user=request.user)
        json_data = json.loads(request.body)
        intent = None
        try:
            if 'payment_method_id' in request.body:
                # Create the PaymentIntent
                intent = stripe.PaymentIntent.create(
                    payment_method=str(json_data['payment_method_id']),
                    amount=ticket_type.price * 100,
                    currency='nok',
                    description=description,
                    confirmation_method='manual',
                    confirm=True,
                )
            elif 'payment_intent_id' in request.body:
                intent = stripe.PaymentIntent.confirm(str(json_data['payment_intent_id']))
        except stripe.error.CardError as e:
            messages.error(request, _(e.user_message))
            return JsonResponse({'error': e.user_message})

        return generate_payment_response(request, ticket_type, intent)

    return HttpResponseRedirect('/')

def generate_payment_response(request, ticket_type, intent):
    # Instruct Stripe.js to handle SCA if required
    if intent.status == 'requires_action' and intent.next_action.type == 'use_stripe_sdk':
        return JsonResponse({
            'requires_action': True,
            'payment_intent_client_secret': intent.client_secret,
        })
    elif intent.status == 'succeeded':

        ticket = Ticket()
        ticket.user = request.user
        ticket.ticket_type = ticket_type
        ticket.bought_date = datetime.now()
        ticket.save()

        lan = ticket.ticket_type.lan
        lan_link = request.build_absolute_uri(reverse('lan_details', kwargs={'lan_id': lan.id}))
        context = {
            'ticket': ticket,
            'lan': lan,
            'lan_link': lan_link,
        }
        txt_message = render_to_string('payment/email/ticket_receipt.txt', context, request).strip()
        html_message = render_to_string('payment/email/ticket_receipt.html', context, request).strip()
        send_mail(
            subject=_(u'Ticket confirmation'),
            from_email=settings.STUDLAN_FROM_MAIL,
            recipient_list=[ticket.user.email],
            message=txt_message,
            html_message=html_message,
        )

        messages.success(
            request,
            _(u'Payment complete.'),
        )

        return JsonResponse({'success': True})
    else:
        messages.error(request, _(u'Payment unsuccessful. Please contact support.'))
        return JsonResponse({'error': 'Invalid PaymentIntent status'})
<|endoftext|>


<commit_before>
import discord
from discord.ext import commands

import collections
import copy
import inspect
import json
import random

import clients
import credentials
from modules import maze
from modules import utilities
from utilities import checks
from utilities import errors

def setup(bot):
	bot.add_cog(Reactions(bot))

	async def process_reactions(reaction, user):
		await bot.cogs["Reactions"].reaction_messages[reaction.message.id](reaction, user)
		with open(clients.data_path + "/stats.json", 'r') as stats_file:
			stats = json.load(stats_file)
		stats["reaction_responses"] += 1
		with open(clients.data_path + "/stats.json", 'w') as stats_file:
			json.dump(stats, stats_file, indent = 4)

	@bot.event
	async def on_reaction_add(reaction, user):
		if "Reactions" in bot.cogs and reaction.message.id in bot.cogs["Reactions"].reaction_messages:
			await process_reactions(reaction, user)

	@bot.event
	async def on_reaction_remove(reaction, user):
		if "Reactions" in bot.cogs and reaction.message.id in bot.cogs["Reactions"].reaction_messages:
			await process_reactions(reaction, user)

class Reactions:

	def __init__(self, bot):
		self.bot = bot
		self.reaction_messages = {}
		self.mazes = {}
		self.numbers = {'\N{KEYCAP TEN}': 10}
		for number in range(9):
			self.numbers[chr(ord('\u0031') + number) + '\N{COMBINING ENCLOSING KEYCAP}'] = number + 1 # '\u0031' - 1
		self.arrows = collections.OrderedDict([('\N{LEFTWARDS BLACK ARROW}', 'W'), ('\N{UPWARDS BLACK ARROW}', 'N'), ('\N{DOWNWARDS BLACK ARROW}', 'S'), ('\N{BLACK RIGHTWARDS ARROW}', 'E')]) # tuple?
		self.controls = collections.OrderedDict([('\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}', "pause_resume"), ('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', "skip"), ('\N{CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY}', "replay"), ('\N{TWISTED RIGHTWARDS ARROWS}', "shuffle"), ('\N{RADIO}', "radio"), ('\N{SPEAKER WITH ONE SOUND WAVE}', "volume_down"), ('\N{SPEAKER WITH THREE SOUND WAVES}', "volume_up")])
		self.reaction_commands = ((self.guessr, "Games.guess"), (self.newsr, "Resources.news"), (self.mazer, "Games.maze"), (self.playingr, "Audio.playing"))
		for command, parent_name in self.reaction_commands:
			utilities.add_as_subcommand(self, command, parent_name, "reactions", aliases = ["reaction", 'r'])

	def __unload(self):
		for command, parent_name in self.reaction_commands:
			utilities.remove_as_subcommand(self, parent_name, "reactions")

	@commands.command(aliases = ["guessreactions", "guessreaction"])
	@checks.not_forbidden()
	async def guessr(self, ctx):
		'''
		Guessing game
		With reactions
		'''
		guess_message = await ctx.embed_reply("Guess a number between 1 to 10")
		embed = guess_message.embeds[0]
		answer = random.randint(1, 10)
		for number_emote in sorted(self.numbers.keys()):
			await guess_message.add_reaction(number_emote)
		self.reaction_messages[guess_message.id] = lambda reaction, user: self.guessr_processr(ctx.author, answer, embed, reaction, user)

	async def guessr_processr(self, player, answer, embed, reaction, user):
		if user == player and reaction.emoji in self.numbers:
			if self.numbers[reaction.emoji] == answer:
				embed.description = "{}: It was {}!".format(player.display_name, self.numbers[reaction.emoji])
				await reaction.message.edit(embed = embed)
				del self.reaction_messages[reaction.message.id]
			else:
				embed.description = "{}: Guess a number between 1 to 10. No, it's not {}".format(player.display_name, self.numbers[reaction.emoji])
				await reaction.message.edit(embed = embed)

	@commands.command(invoke_without_command = True)
	@checks.not_forbidden()
	async def newsr(self, ctx, source : str):
		'''
		News
		With Reactions
		Powered by NewsAPI.org
		'''
		async with clients.aiohttp_session.get("https://newsapi.org/v1/articles?source={}&apiKey={}".format(source, credentials.news_api_key)) as resp:
			data = await resp.json()
		if data["status"] != "ok":
			await ctx.embed_reply(":no_entry: Error: {}".format(data["message"]))
			return
		response = await ctx.reply("React with a number from 1 to 10 to view each news article")
		numbers = {'\N{KEYCAP TEN}': 10}
		for number in range(9):
			numbers[chr(ord('\u0031') + number) + '\N{COMBINING ENCLOSING KEYCAP}'] = number + 1 # '\u0031' - 1
		for number_emote in sorted(numbers.keys()):
			await response.add_reaction(number_emote)
		while True:
			emoji_response = await self.bot.wait_for_reaction(user = ctx.author, message = response, emoji = numbers.keys())
			reaction = emoji_response.reaction
			number = numbers[reaction.emoji]
			article = data["articles"][number - 1]
			output = "Article {}:".format(number)
			output += "\n**{}**".format(article["title"])
			if article.get("publishedAt"):
				output += " ({})".format(article.get("publishedAt").replace('T', " ").replace('Z', ""))
			# output += "\n{}".format(article["description"])
			# output += "\n<{}>".format(article["url"])
			output += "\n{}".format(article["url"])
			output += "\nSelect a different number for another article"
			await self.bot.edit_message(response, "{}: {}".format(ctx.author.display_name, output))

	# TODO: urband
	# TODO: rtg

	@commands.command()
	@checks.not_forbidden()
	async def mazer(self, ctx, width : int = 5, height : int = 5, random_start : bool = False, random_end : bool = False):
		'''
		Maze game
		With reactions
		width: 2 - 100
		height: 2 - 100
		React with an arrow key to move
		'''
		maze_instance = maze.Maze(width, height, random_start = random_start, random_end = random_end)
		maze_message = await ctx.embed_reply(clients.code_block.format(maze_instance.print_visible()), footer_text = "Your current position: {}, {}".format(maze_instance.column + 1, maze_instance.row + 1))
		self.mazes[maze_message.id] = maze_instance
		for emote in tuple(self.arrows.keys()) + ("\N{PRINTER}",):
			await self.bot.add_reaction(maze_message, emote)
		self.reaction_messages[maze_message.id] = lambda reaction, user: self.mazer_processr(ctx.author, reaction, user)

	async def mazer_processr(self, player, reaction, user):
		if user == player and reaction.emoji in tuple(self.arrows.keys()) + ("\N{PRINTER}",):
			maze_instance = self.mazes[reaction.message.id]
			if reaction.emoji == "\N{PRINTER}":
				with open(clients.data_path + "/temp/maze.txt", 'w') as maze_file:
					maze_file.write('\n'.join(maze_instance.visible))
				await self.bot.send_file(reaction.message.channel, clients.data_path + "/temp/maze.txt", content = "{}:\nYour maze is attached".format(player.display_name))
				return
			embed = discord.Embed(color = clients.bot_color)
			embed.set_author(name = player.display_name, icon_url = player.avatar_url)
			moved = maze_instance.move(self.arrows[reaction.emoji].lower())
			embed.set_footer(text = "Your current position: {}, {}".format(maze_instance.column + 1, maze_instance.row + 1))
			if moved:
				if maze_instance.reached_end():
					embed.description = "{}\nCongratulations! You reached the end of the maze in {} moves".format(clients.code_block.format(maze_instance.print_visible()), maze_instance.move_counter)
					del self.reaction_messages[reaction.message.id]
				else:
					embed.description = "{}".format(clients.code_block.format(maze_instance.print_visible()))
			else:
				embed.description = "{}\n:no_entry: You can't go that way".format(clients.code_block.format(maze_instance.print_visible()))
			await self.bot.edit_message(reaction.message, embed = embed)

	@commands.command(aliases = ["player"])
	@commands.guild_only()
	@checks.not_forbidden()
	async def playingr(self, ctx):
		'''Audio player'''
		try:
			embed = self.bot.cogs["Audio"].players[ctx.guild.id].current_embed()
		except errors.AudioNotPlaying:
			player_message = await ctx.embed_reply(":speaker: There is no song currently playing")
		else:
			embed.set_author(name = ctx.author.display_name, icon_url = ctx.author.avatar_url)
			player_message, embed = await self.bot.say(embed = embed)
		await self.bot.attempt_delete_message(ctx.message)
		for control_emote in self.controls.keys():
			await self.bot.add_reaction(player_message, control_emote)
		self.reaction_messages[player_message.id] = lambda reaction, user: self.playingr_processr(ctx, reaction, user)

	# TODO: Queue?, Empty?, Settext?, Other?
	# TODO: Resend player?

	async def playingr_processr(self, ctx, reaction, user):
		if reaction.emoji in self.controls:
			if self.controls[reaction.emoji] == "pause_resume":
				if utilities.get_permission(ctx, "pause", id = user.id) or user == ctx.guild.owner or user.id == self.bot.owner_id:
					embed = discord.Embed(color = clients.bot_color).set_author(name = user.display_name, icon_url = user.avatar_url)
					try:
						self.bot.cogs["Audio"].players[ctx.guild.id].pause()
					except errors.AudioNotPlaying:
						embed.description = ":no_entry: There is no song to pause"
					except errors.AudioAlreadyDone:
						self.bot.cogs["Audio"].players[ctx.guild.id].resume()
						embed.description = ":play_pause: Resumed song"
					else:
						embed.description = ":pause_button: Paused song"
					await self.bot.send_message(ctx.channel, embed = embed)
					await self.bot.attempt_delete_message(ctx.message)
			elif self.controls[reaction.emoji] in ("skip", "replay", "shuffle", "radio"):
				if utilities.get_permission(ctx, self.controls[reaction.emoji], id = user.id) or user.id in (ctx.guild.owner.id, self.bot.owner_id):
					message = copy.copy(ctx.message)
					message.content = "{}{}".format(ctx.prefix, self.controls[reaction.emoji])
					await self.bot.process_commands(message)
					# Timestamp for radio
			elif self.controls[reaction.emoji] in ("volume_down", "volume_up"):
				if utilities.get_permission(ctx, "volume", id = user.id) or user.id in (ctx.guild.owner, self.bot.owner_id):
					try:
						current_volume = self.bot.cogs["Audio"].players[ctx.guild.id].get_volume()
					except errors.AudioNotPlaying:
						await ctx.embed_reply(":no_entry: Couldn't change volume\nThere's nothing playing right now")
					if self.controls[reaction.emoji] == "volume_down": set_volume = current_volume - 10
					elif self.controls[reaction.emoji] == "volume_up": set_volume = current_volume + 10
					message = copy.copy(ctx.message)
					message.content = "{}volume {}".format(ctx.prefix, set_volume)
					await self.bot.process_commands(message)

<commit_msg>Updated news reactions command message edit method<commit_after>
import discord
from discord.ext import commands

import collections
import copy
import inspect
import json
import random

import clients
import credentials
from modules import maze
from modules import utilities
from utilities import checks
from utilities import errors

def setup(bot):
	bot.add_cog(Reactions(bot))

	async def process_reactions(reaction, user):
		await bot.cogs["Reactions"].reaction_messages[reaction.message.id](reaction, user)
		with open(clients.data_path + "/stats.json", 'r') as stats_file:
			stats = json.load(stats_file)
		stats["reaction_responses"] += 1
		with open(clients.data_path + "/stats.json", 'w') as stats_file:
			json.dump(stats, stats_file, indent = 4)

	@bot.event
	async def on_reaction_add(reaction, user):
		if "Reactions" in bot.cogs and reaction.message.id in bot.cogs["Reactions"].reaction_messages:
			await process_reactions(reaction, user)

	@bot.event
	async def on_reaction_remove(reaction, user):
		if "Reactions" in bot.cogs and reaction.message.id in bot.cogs["Reactions"].reaction_messages:
			await process_reactions(reaction, user)

class Reactions:

	def __init__(self, bot):
		self.bot = bot
		self.reaction_messages = {}
		self.mazes = {}
		self.numbers = {'\N{KEYCAP TEN}': 10}
		for number in range(9):
			self.numbers[chr(ord('\u0031') + number) + '\N{COMBINING ENCLOSING KEYCAP}'] = number + 1 # '\u0031' - 1
		self.arrows = collections.OrderedDict([('\N{LEFTWARDS BLACK ARROW}', 'W'), ('\N{UPWARDS BLACK ARROW}', 'N'), ('\N{DOWNWARDS BLACK ARROW}', 'S'), ('\N{BLACK RIGHTWARDS ARROW}', 'E')]) # tuple?
		self.controls = collections.OrderedDict([('\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}', "pause_resume"), ('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', "skip"), ('\N{CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY}', "replay"), ('\N{TWISTED RIGHTWARDS ARROWS}', "shuffle"), ('\N{RADIO}', "radio"), ('\N{SPEAKER WITH ONE SOUND WAVE}', "volume_down"), ('\N{SPEAKER WITH THREE SOUND WAVES}', "volume_up")])
		self.reaction_commands = ((self.guessr, "Games.guess"), (self.newsr, "Resources.news"), (self.mazer, "Games.maze"), (self.playingr, "Audio.playing"))
		for command, parent_name in self.reaction_commands:
			utilities.add_as_subcommand(self, command, parent_name, "reactions", aliases = ["reaction", 'r'])

	def __unload(self):
		for command, parent_name in self.reaction_commands:
			utilities.remove_as_subcommand(self, parent_name, "reactions")

	@commands.command(aliases = ["guessreactions", "guessreaction"])
	@checks.not_forbidden()
	async def guessr(self, ctx):
		'''
		Guessing game
		With reactions
		'''
		guess_message = await ctx.embed_reply("Guess a number between 1 to 10")
		embed = guess_message.embeds[0]
		answer = random.randint(1, 10)
		for number_emote in sorted(self.numbers.keys()):
			await guess_message.add_reaction(number_emote)
		self.reaction_messages[guess_message.id] = lambda reaction, user: self.guessr_processr(ctx.author, answer, embed, reaction, user)

	async def guessr_processr(self, player, answer, embed, reaction, user):
		if user == player and reaction.emoji in self.numbers:
			if self.numbers[reaction.emoji] == answer:
				embed.description = "{}: It was {}!".format(player.display_name, self.numbers[reaction.emoji])
				await reaction.message.edit(embed = embed)
				del self.reaction_messages[reaction.message.id]
			else:
				embed.description = "{}: Guess a number between 1 to 10. No, it's not {}".format(player.display_name, self.numbers[reaction.emoji])
				await reaction.message.edit(embed = embed)

	@commands.command(invoke_without_command = True)
	@checks.not_forbidden()
	async def newsr(self, ctx, source : str):
		'''
		News
		With Reactions
		Powered by NewsAPI.org
		'''
		async with clients.aiohttp_session.get("https://newsapi.org/v1/articles?source={}&apiKey={}".format(source, credentials.news_api_key)) as resp:
			data = await resp.json()
		if data["status"] != "ok":
			await ctx.embed_reply(":no_entry: Error: {}".format(data["message"]))
			return
		response = await ctx.reply("React with a number from 1 to 10 to view each news article")
		numbers = {'\N{KEYCAP TEN}': 10}
		for number in range(9):
			numbers[chr(ord('\u0031') + number) + '\N{COMBINING ENCLOSING KEYCAP}'] = number + 1 # '\u0031' - 1
		for number_emote in sorted(numbers.keys()):
			await response.add_reaction(number_emote)
		while True:
			emoji_response = await self.bot.wait_for_reaction(user = ctx.author, message = response, emoji = numbers.keys())
			reaction = emoji_response.reaction
			number = numbers[reaction.emoji]
			article = data["articles"][number - 1]
			output = "Article {}:".format(number)
			output += "\n**{}**".format(article["title"])
			if article.get("publishedAt"):
				output += " ({})".format(article.get("publishedAt").replace('T', " ").replace('Z', ""))
			# output += "\n{}".format(article["description"])
			# output += "\n<{}>".format(article["url"])
			output += "\n{}".format(article["url"])
			output += "\nSelect a different number for another article"
			await response.edit(content = "{}: {}".format(ctx.author.display_name, output))

	# TODO: urband
	# TODO: rtg

	@commands.command()
	@checks.not_forbidden()
	async def mazer(self, ctx, width : int = 5, height : int = 5, random_start : bool = False, random_end : bool = False):
		'''
		Maze game
		With reactions
		width: 2 - 100
		height: 2 - 100
		React with an arrow key to move
		'''
		maze_instance = maze.Maze(width, height, random_start = random_start, random_end = random_end)
		maze_message = await ctx.embed_reply(clients.code_block.format(maze_instance.print_visible()), footer_text = "Your current position: {}, {}".format(maze_instance.column + 1, maze_instance.row + 1))
		self.mazes[maze_message.id] = maze_instance
		for emote in tuple(self.arrows.keys()) + ("\N{PRINTER}",):
			await self.bot.add_reaction(maze_message, emote)
		self.reaction_messages[maze_message.id] = lambda reaction, user: self.mazer_processr(ctx.author, reaction, user)

	async def mazer_processr(self, player, reaction, user):
		if user == player and reaction.emoji in tuple(self.arrows.keys()) + ("\N{PRINTER}",):
			maze_instance = self.mazes[reaction.message.id]
			if reaction.emoji == "\N{PRINTER}":
				with open(clients.data_path + "/temp/maze.txt", 'w') as maze_file:
					maze_file.write('\n'.join(maze_instance.visible))
				await self.bot.send_file(reaction.message.channel, clients.data_path + "/temp/maze.txt", content = "{}:\nYour maze is attached".format(player.display_name))
				return
			embed = discord.Embed(color = clients.bot_color)
			embed.set_author(name = player.display_name, icon_url = player.avatar_url)
			moved = maze_instance.move(self.arrows[reaction.emoji].lower())
			embed.set_footer(text = "Your current position: {}, {}".format(maze_instance.column + 1, maze_instance.row + 1))
			if moved:
				if maze_instance.reached_end():
					embed.description = "{}\nCongratulations! You reached the end of the maze in {} moves".format(clients.code_block.format(maze_instance.print_visible()), maze_instance.move_counter)
					del self.reaction_messages[reaction.message.id]
				else:
					embed.description = "{}".format(clients.code_block.format(maze_instance.print_visible()))
			else:
				embed.description = "{}\n:no_entry: You can't go that way".format(clients.code_block.format(maze_instance.print_visible()))
			await self.bot.edit_message(reaction.message, embed = embed)

	@commands.command(aliases = ["player"])
	@commands.guild_only()
	@checks.not_forbidden()
	async def playingr(self, ctx):
		'''Audio player'''
		try:
			embed = self.bot.cogs["Audio"].players[ctx.guild.id].current_embed()
		except errors.AudioNotPlaying:
			player_message = await ctx.embed_reply(":speaker: There is no song currently playing")
		else:
			embed.set_author(name = ctx.author.display_name, icon_url = ctx.author.avatar_url)
			player_message, embed = await self.bot.say(embed = embed)
		await self.bot.attempt_delete_message(ctx.message)
		for control_emote in self.controls.keys():
			await self.bot.add_reaction(player_message, control_emote)
		self.reaction_messages[player_message.id] = lambda reaction, user: self.playingr_processr(ctx, reaction, user)

	# TODO: Queue?, Empty?, Settext?, Other?
	# TODO: Resend player?

	async def playingr_processr(self, ctx, reaction, user):
		if reaction.emoji in self.controls:
			if self.controls[reaction.emoji] == "pause_resume":
				if utilities.get_permission(ctx, "pause", id = user.id) or user == ctx.guild.owner or user.id == self.bot.owner_id:
					embed = discord.Embed(color = clients.bot_color).set_author(name = user.display_name, icon_url = user.avatar_url)
					try:
						self.bot.cogs["Audio"].players[ctx.guild.id].pause()
					except errors.AudioNotPlaying:
						embed.description = ":no_entry: There is no song to pause"
					except errors.AudioAlreadyDone:
						self.bot.cogs["Audio"].players[ctx.guild.id].resume()
						embed.description = ":play_pause: Resumed song"
					else:
						embed.description = ":pause_button: Paused song"
					await self.bot.send_message(ctx.channel, embed = embed)
					await self.bot.attempt_delete_message(ctx.message)
			elif self.controls[reaction.emoji] in ("skip", "replay", "shuffle", "radio"):
				if utilities.get_permission(ctx, self.controls[reaction.emoji], id = user.id) or user.id in (ctx.guild.owner.id, self.bot.owner_id):
					message = copy.copy(ctx.message)
					message.content = "{}{}".format(ctx.prefix, self.controls[reaction.emoji])
					await self.bot.process_commands(message)
					# Timestamp for radio
			elif self.controls[reaction.emoji] in ("volume_down", "volume_up"):
				if utilities.get_permission(ctx, "volume", id = user.id) or user.id in (ctx.guild.owner, self.bot.owner_id):
					try:
						current_volume = self.bot.cogs["Audio"].players[ctx.guild.id].get_volume()
					except errors.AudioNotPlaying:
						await ctx.embed_reply(":no_entry: Couldn't change volume\nThere's nothing playing right now")
					if self.controls[reaction.emoji] == "volume_down": set_volume = current_volume - 10
					elif self.controls[reaction.emoji] == "volume_up": set_volume = current_volume + 10
					message = copy.copy(ctx.message)
					message.content = "{}volume {}".format(ctx.prefix, set_volume)
					await self.bot.process_commands(message)

<|endoftext|>


<commit_before>import datetime
import mongoengine as mongo
from utils import log as logging
from django.db import models
from django.contrib.auth.models import User
from django.core.cache import cache
from apps.rss_feeds.models import Feed, Story, MStory
from apps.analyzer.models import MClassifierFeed, MClassifierAuthor, MClassifierTag, MClassifierTitle
from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds, apply_classifier_authors, apply_classifier_tags

DAYS_OF_UNREAD = 14
MONTH_AGO = datetime.datetime.now() - datetime.timedelta(days=30)

class UserSubscription(models.Model):
    """
    A feed which a user has subscrubed to. Carries all of the cached information
    about the subscription, including unread counts of the three primary scores.

    Also has a dirty flag (needs_unread_recalc) which means that the unread counts
    are not accurate and need to be calculated with `self.calculate_feed_scores()`.
    """
    user = models.ForeignKey(User, related_name='subscriptions')
    feed = models.ForeignKey(Feed, related_name='subscribers')
    last_read_date = models.DateTimeField(default=datetime.datetime.now()
                                                  - datetime.timedelta(days=DAYS_OF_UNREAD))
    mark_read_date = models.DateTimeField(default=datetime.datetime.now()
                                                 - datetime.timedelta(days=DAYS_OF_UNREAD))
    unread_count_neutral = models.IntegerField(default=0)
    unread_count_positive = models.IntegerField(default=0)
    unread_count_negative = models.IntegerField(default=0)
    unread_count_updated = models.DateTimeField(auto_now=True)
    needs_unread_recalc = models.BooleanField(default=False)
    feed_opens = models.IntegerField(default=0)
    is_trained = models.BooleanField(default=False)

    def __unicode__(self):
        return '[' + self.feed.feed_title + '] '

    def mark_feed_read(self):
        now = datetime.datetime.utcnow()
        if MStory.objects(story_feed_id=self.feed.pk).first():
            latest_story_date = MStory.objects(story_feed_id=self.feed.pk).order_by('-story_date')[0].story_date\
                                + datetime.timedelta(minutes=1)
        else:
            latest_story_date = now
        self.last_read_date = max(now, latest_story_date)
        self.mark_read_date = max(now, latest_story_date)
        self.unread_count_negative = 0
        self.unread_count_positive = 0
        self.unread_count_neutral = 0
        self.unread_count_updated = max(now, latest_story_date)
        self.needs_unread_relcalc = False
        self.save()

    def calculate_feed_scores(self, silent=False):
        if self.user.profile.last_seen_on < MONTH_AGO:
            if not silent:
                logging.info(' ---> [%s] SKIPPING Computing scores: %s (1 month+)' % (self.user, self.feed))
            return

        if not self.feed.fetched_once:
            if not silent:
                logging.info(' ---> [%s] NOT Computing scores: %s' % (self.user, self.feed))
            self.needs_unread_recalc = False
            self.save()
            return

        if not silent:
            logging.info(' ---> [%s] Computing scores: %s' % (self.user, self.feed))
        feed_scores = dict(negative=0, neutral=0, positive=0)

        # Two weeks in age. If mark_read_date is older, mark old stories as read.
        date_delta = datetime.datetime.utcnow()-datetime.timedelta(days=DAYS_OF_UNREAD)
        if date_delta < self.mark_read_date:
            date_delta = self.mark_read_date
        else:
            self.mark_read_date = date_delta

        read_stories = MUserStory.objects(user_id=self.user.pk,
                                          feed_id=self.feed.pk,
                                          read_date__gte=self.mark_read_date)
        read_stories_ids = []
        for us in read_stories:
            if hasattr(us.story, 'story_guid') and isinstance(us.story.story_guid, unicode):
                read_stories_ids.append(us.story.story_guid)
            elif hasattr(us.story, 'id') and isinstance(us.story.id, unicode):
                read_stories_ids.append(us.story.id) # TODO: Remove me after migration from story.id->guid
        stories_db = MStory.objects(story_feed_id=self.feed.pk,
                                    story_date__gte=date_delta)
        unread_stories_db = []
        for story in stories_db:
            if hasattr(story, 'story_guid') and story.story_guid not in read_stories_ids:
                unread_stories_db.append(story)
            elif isinstance(story.id, unicode) and story.id not in read_stories_ids:
                unread_stories_db.append(story)
        stories = self.feed.format_stories(unread_stories_db)

        classifier_feeds = MClassifierFeed.objects(user_id=self.user.pk, feed_id=self.feed.pk)
        classifier_authors = MClassifierAuthor.objects(user_id=self.user.pk, feed_id=self.feed.pk)
        classifier_titles = MClassifierTitle.objects(user_id=self.user.pk, feed_id=self.feed.pk)
        classifier_tags = MClassifierTag.objects(user_id=self.user.pk, feed_id=self.feed.pk)

        scores = {
            'feed': apply_classifier_feeds(classifier_feeds, self.feed),
        }

        for story in stories:
            classifier_authors.rewind()
            classifier_tags.rewind()
            classifier_titles.rewind()
            scores.update({
                'author': apply_classifier_authors(classifier_authors, story),
                'tags': apply_classifier_tags(classifier_tags, story),
                'title': apply_classifier_titles(classifier_titles, story),
            })

            max_score = max(scores['feed'], scores['author'], scores['tags'], scores['title'])
            min_score = min(scores['feed'], scores['author'], scores['tags'], scores['title'])
            if max_score > 0:
                feed_scores['positive'] += 1
            if min_score < 0:
                feed_scores['negative'] += 1
            if max_score == 0 and min_score == 0:
                feed_scores['neutral'] += 1

        self.unread_count_positive = feed_scores['positive']
        self.unread_count_neutral = feed_scores['neutral']
        self.unread_count_negative = feed_scores['negative']
        self.needs_unread_recalc = False

        self.save()

        if (self.unread_count_positive == 0 and
            self.unread_count_neutral == 0):
            self.mark_feed_read()

        cache.delete('usersub:%s' % self.user.id)

        return

    class Meta:
        unique_together = ("user", "feed")

class UserStory(models.Model):
    """
    Stories read by the user. These are deleted as the mark_read_date for the
    UserSubscription passes the UserStory date.
    """
    user = models.ForeignKey(User)
    feed = models.ForeignKey(Feed)
    story = models.ForeignKey(Story)
    read_date = models.DateTimeField(auto_now=True)
    opinion = models.IntegerField(default=0)

    def __unicode__(self):
        return ('[' + self.feed.feed_title + '] '
                + self.story.story_title)

    class Meta:
        verbose_name_plural = "user stories"
        verbose_name = "user story"
        unique_together = ("user", "feed", "story")

class MUserStory(mongo.Document):
    """
    Stories read by the user. These are deleted as the mark_read_date for the
    UserSubscription passes the UserStory date.
    """
    user_id = mongo.IntField()
    feed_id = mongo.IntField()
    read_date = mongo.DateTimeField()
    story = mongo.ReferenceField(MStory, unique_with=('user_id', 'feed_id'))

    meta = {
        'collection': 'userstories',
        'indexes': [('user_id', 'feed_id')],
        'allow_inheritance': False,
    }

class UserSubscriptionFolders(models.Model):
    """
    A JSON list of folders and feeds for while a user has subscribed. The list
    is a recursive descent of feeds and folders in folders. Used to layout
    the feeds and folders in the Reader's feed navigation pane.
    """
    user = models.ForeignKey(User)
    folders = models.TextField(default="[]")

    def __unicode__(self):
        return "[%s]: %s" % (self.user, len(self.folders),)

    class Meta:
        verbose_name_plural = "folders"
        verbose_name = "folder"

class Feature(models.Model):
    """
    Simple blog-like feature board shown to all users on the home page.
    """
    description = models.TextField(default="")
    date = models.DateTimeField(default=datetime.datetime.now)

    def __unicode__(self):
        return "[%s] %s" % (self.date, self.description[:50])

    class Meta:
        ordering = ["-date"]
        <commit_msg>Adding extra logging to calculate_feed_scores for debugging purposes.<commit_after>import datetime
import mongoengine as mongo
from utils import log as logging
from django.db import models
from django.contrib.auth.models import User
from django.core.cache import cache
from apps.rss_feeds.models import Feed, Story, MStory
from apps.analyzer.models import MClassifierFeed, MClassifierAuthor, MClassifierTag, MClassifierTitle
from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds, apply_classifier_authors, apply_classifier_tags

DAYS_OF_UNREAD = 14
MONTH_AGO = datetime.datetime.now() - datetime.timedelta(days=30)

class UserSubscription(models.Model):
    """
    A feed which a user has subscrubed to. Carries all of the cached information
    about the subscription, including unread counts of the three primary scores.

    Also has a dirty flag (needs_unread_recalc) which means that the unread counts
    are not accurate and need to be calculated with `self.calculate_feed_scores()`.
    """
    user = models.ForeignKey(User, related_name='subscriptions')
    feed = models.ForeignKey(Feed, related_name='subscribers')
    last_read_date = models.DateTimeField(default=datetime.datetime.now()
                                                  - datetime.timedelta(days=DAYS_OF_UNREAD))
    mark_read_date = models.DateTimeField(default=datetime.datetime.now()
                                                 - datetime.timedelta(days=DAYS_OF_UNREAD))
    unread_count_neutral = models.IntegerField(default=0)
    unread_count_positive = models.IntegerField(default=0)
    unread_count_negative = models.IntegerField(default=0)
    unread_count_updated = models.DateTimeField(auto_now=True)
    needs_unread_recalc = models.BooleanField(default=False)
    feed_opens = models.IntegerField(default=0)
    is_trained = models.BooleanField(default=False)

    def __unicode__(self):
        return '[' + self.feed.feed_title + '] '

    def mark_feed_read(self):
        now = datetime.datetime.utcnow()
        if MStory.objects(story_feed_id=self.feed.pk).first():
            latest_story_date = MStory.objects(story_feed_id=self.feed.pk).order_by('-story_date')[0].story_date\
                                + datetime.timedelta(minutes=1)
        else:
            latest_story_date = now
        self.last_read_date = max(now, latest_story_date)
        self.mark_read_date = max(now, latest_story_date)
        self.unread_count_negative = 0
        self.unread_count_positive = 0
        self.unread_count_neutral = 0
        self.unread_count_updated = max(now, latest_story_date)
        self.needs_unread_relcalc = False
        self.save()

    def calculate_feed_scores(self, silent=False):
        if self.user.profile.last_seen_on < MONTH_AGO:
            if not silent:
                logging.info(' ---> [%s] SKIPPING Computing scores: %s (1 month+)' % (self.user, self.feed))
            return

        if not self.feed.fetched_once:
            if not silent:
                logging.info(' ---> [%s] NOT Computing scores: %s' % (self.user, self.feed))
            self.needs_unread_recalc = False
            self.save()
            return
        now = datetime.datetime.now()
        if not silent:
            logging.info(' ---> [%s] Computing scores: %s' % (self.user, self.feed))
        feed_scores = dict(negative=0, neutral=0, positive=0)

        # Two weeks in age. If mark_read_date is older, mark old stories as read.
        date_delta = datetime.datetime.utcnow()-datetime.timedelta(days=DAYS_OF_UNREAD)
        if date_delta < self.mark_read_date:
            date_delta = self.mark_read_date
        else:
            self.mark_read_date = date_delta

        read_stories = MUserStory.objects(user_id=self.user.pk,
                                          feed_id=self.feed.pk,
                                          read_date__gte=self.mark_read_date)
        if not silent:
            logging.info(' ---> [%s]    Read stories: %s' % (datetime.datetime.now() - now))
        read_stories_ids = []
        for us in read_stories:
            if hasattr(us.story, 'story_guid') and isinstance(us.story.story_guid, unicode):
                read_stories_ids.append(us.story.story_guid)
            elif hasattr(us.story, 'id') and isinstance(us.story.id, unicode):
                read_stories_ids.append(us.story.id) # TODO: Remove me after migration from story.id->guid
        stories_db = MStory.objects(story_feed_id=self.feed.pk,
                                    story_date__gte=date_delta)
        if not silent:
            logging.info(' ---> [%s]    MStory: %s' % (datetime.datetime.now() - now))
        unread_stories_db = []
        for story in stories_db:
            if hasattr(story, 'story_guid') and story.story_guid not in read_stories_ids:
                unread_stories_db.append(story)
            elif isinstance(story.id, unicode) and story.id not in read_stories_ids:
                unread_stories_db.append(story)
        stories = self.feed.format_stories(unread_stories_db)
        if not silent:
            logging.info(' ---> [%s]    Format stories: %s' % (datetime.datetime.now() - now))

        classifier_feeds = MClassifierFeed.objects(user_id=self.user.pk, feed_id=self.feed.pk)
        classifier_authors = MClassifierAuthor.objects(user_id=self.user.pk, feed_id=self.feed.pk)
        classifier_titles = MClassifierTitle.objects(user_id=self.user.pk, feed_id=self.feed.pk)
        classifier_tags = MClassifierTag.objects(user_id=self.user.pk, feed_id=self.feed.pk)

        if not silent:
            logging.info(' ---> [%s]    Classifiers: %s' % (datetime.datetime.now() - now))

        scores = {
            'feed': apply_classifier_feeds(classifier_feeds, self.feed),
        }

        for story in stories:
            classifier_authors.rewind()
            classifier_tags.rewind()
            classifier_titles.rewind()
            scores.update({
                'author': apply_classifier_authors(classifier_authors, story),
                'tags': apply_classifier_tags(classifier_tags, story),
                'title': apply_classifier_titles(classifier_titles, story),
            })

            max_score = max(scores['feed'], scores['author'], scores['tags'], scores['title'])
            min_score = min(scores['feed'], scores['author'], scores['tags'], scores['title'])
            if max_score > 0:
                feed_scores['positive'] += 1
            if min_score < 0:
                feed_scores['negative'] += 1
            if max_score == 0 and min_score == 0:
                feed_scores['neutral'] += 1

        if not silent:
            logging.info(' ---> [%s]    End classifiers: %s' % (datetime.datetime.now() - now))

        self.unread_count_positive = feed_scores['positive']
        self.unread_count_neutral = feed_scores['neutral']
        self.unread_count_negative = feed_scores['negative']
        self.needs_unread_recalc = False

        self.save()

        if (self.unread_count_positive == 0 and
            self.unread_count_neutral == 0):
            self.mark_feed_read()

        cache.delete('usersub:%s' % self.user.id)

        return

    class Meta:
        unique_together = ("user", "feed")

class UserStory(models.Model):
    """
    Stories read by the user. These are deleted as the mark_read_date for the
    UserSubscription passes the UserStory date.
    """
    user = models.ForeignKey(User)
    feed = models.ForeignKey(Feed)
    story = models.ForeignKey(Story)
    read_date = models.DateTimeField(auto_now=True)
    opinion = models.IntegerField(default=0)

    def __unicode__(self):
        return ('[' + self.feed.feed_title + '] '
                + self.story.story_title)

    class Meta:
        verbose_name_plural = "user stories"
        verbose_name = "user story"
        unique_together = ("user", "feed", "story")

class MUserStory(mongo.Document):
    """
    Stories read by the user. These are deleted as the mark_read_date for the
    UserSubscription passes the UserStory date.
    """
    user_id = mongo.IntField()
    feed_id = mongo.IntField()
    read_date = mongo.DateTimeField()
    story = mongo.ReferenceField(MStory, unique_with=('user_id', 'feed_id'))

    meta = {
        'collection': 'userstories',
        'indexes': [('user_id', 'feed_id')],
        'allow_inheritance': False,
    }

class UserSubscriptionFolders(models.Model):
    """
    A JSON list of folders and feeds for while a user has subscribed. The list
    is a recursive descent of feeds and folders in folders. Used to layout
    the feeds and folders in the Reader's feed navigation pane.
    """
    user = models.ForeignKey(User)
    folders = models.TextField(default="[]")

    def __unicode__(self):
        return "[%s]: %s" % (self.user, len(self.folders),)

    class Meta:
        verbose_name_plural = "folders"
        verbose_name = "folder"

class Feature(models.Model):
    """
    Simple blog-like feature board shown to all users on the home page.
    """
    description = models.TextField(default="")
    date = models.DateTimeField(default=datetime.datetime.now)

    def __unicode__(self):
        return "[%s] %s" % (self.date, self.description[:50])

    class Meta:
        ordering = ["-date"]
        <|endoftext|>