예제 #1
0
파일: product.py 프로젝트: paulowe/djaoapp
 def get_organization(self):
     slug = self.kwargs.get(self.organization_url_kwarg, None)
     if slug is not None:
         queryset = get_organization_model().objects.filter(slug=slug)
         if queryset.exists():
             return queryset.get()
     return get_current_broker()
예제 #2
0
def _send_notification_email(from_site, recipients, template,
                             context=None, reply_to=None, bcc=None):
    """
    Sends a notification e-mail using the current site connection,
    defaulting to sending an e-mail to broker profile managers
    if there is any problem with the connection settings.
    """
    try:
        get_email_backend(connection=from_site.get_email_connection()).send(
            from_email=from_site.get_from_email(),
            recipients=recipients,
            reply_to=reply_to,
            bcc=bcc,
            template=template,
            context=context)
    except smtplib.SMTPException as err:
        LOGGER.warning(
            "[signal] problem sending email from %s on connection for %s. %s",
            from_site.get_from_email(), from_site, err)
        context.update({'errors': [_("There was an error sending"\
" the following email to %(recipients)s. This is most likely due to"\
" a misconfiguration of the e-mail notifications whitelabel settings"\
" for your site %(site)s.") % {
    'recipients': recipients, 'site': from_site.as_absolute_uri()}]})
        #pylint:disable=unused-variable
        notified_on_errors, notused = _notified_recipients(
            get_organization_model().objects.using(from_site._state.db).get(
                pk=from_site.account_id), "")
        if notified_on_errors:
            get_email_backend(
                connection=get_connection_base(fail_silently=True)).send(
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipients=notified_on_errors,
                template=template,
                context=context)
예제 #3
0
    def register(self, **cleaned_data):
        username = cleaned_data['username']
        password = cleaned_data['password']
        first_name = cleaned_data['first_name']
        last_name = cleaned_data['last_name']

        # Create a ``User``
        user = get_user_model().objects.create_user(
            username=username,
            password=password,
            email=cleaned_data['email'],
            first_name=first_name,
            last_name=last_name)

        terms_of_use = 'terms-of-use'
        Signature.objects.create_signature(terms_of_use, user)

        # Create a 'personal' ``Organization`` to associate the user
        # to a billing account.
        account = get_organization_model().objects.create(
            slug=username,
            full_name='%s %s' % (first_name, last_name),
            email=cleaned_data['email'],
            street_address=cleaned_data['street_address'],
            locality=cleaned_data['city'],
            region=cleaned_data['region'],
            postal_code=cleaned_data['zip_code'],
            country=cleaned_data['country'])
        account.add_manager(user)

        # Sign-in the newly registered user
        user = authenticate(username=username, password=password)
        auth_login(self.request, user)

        return user
예제 #4
0
 def get_organization(self):
     try:
         organization = super(ExtraMixin, self).get_organization()
     except Http404:
         # AccountModel is a rules.App because we need the bucket.
         if isinstance(self.account, get_organization_model()):
             organization = self.account
         else:
             organization = self.account.account
     return organization
예제 #5
0
def _send_notification_email(from_site, recipients, template,
                             context=None, reply_to=None, bcc=None):
    """
    Sends a notification e-mail using the current site connection,
    defaulting to sending an e-mail to broker profile managers
    if there is any problem with the connection settings.
    """
    #pylint:disable=too-many-arguments
    lang_code = None
    contact = Contact.objects.filter(
        email__in=recipients).order_by('email').first()
    if contact:
        lang_code = contact.lang
    try:
        with translation.override(lang_code):
            get_email_backend(connection=from_site.get_email_connection()).send(
                from_email=from_site.get_from_email(),
                recipients=recipients,
                reply_to=reply_to,
                bcc=bcc,
                template=template,
                context=context)
    except smtplib.SMTPException as err:
        LOGGER.warning(
            "[signal] problem sending email from %s on connection for %s. %s",
            from_site.get_from_email(), from_site, err)
        context.update({'errors': [_("There was an error sending"\
" the following email to %(recipients)s. This is most likely due to"\
" a misconfiguration of the e-mail notifications whitelabel settings"\
" for your site %(site)s.") % {
    'recipients': recipients, 'site': from_site.as_absolute_uri()}]})
        #pylint:disable=unused-variable
        notified_on_errors, notused = _notified_recipients(
            get_organization_model().objects.using(from_site._state.db).get(
                pk=from_site.account_id), "")
        if notified_on_errors:
            get_email_backend(
                connection=get_connection_base(fail_silently=True)).send(
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipients=notified_on_errors,
                template=template,
                context=context)
    except Exception as err:
        # Something went horribly wrong, like the email password was not
        # decrypted correctly. We want to notifiy the operations team
        # but the end user shouldn't see a 500 error as a result
        # of notifications sent in the HTTP request pipeline.
        LOGGER.exception(err)
예제 #6
0
    def clean_username(self):
        """
        Validate that the username is not already taken.
        """
        user = get_user_model().objects.filter(
            username=self.cleaned_data['username'])
        if user.exists():
            raise forms.ValidationError(
                "A user with that username already exists.")
        organization = get_organization_model().objects.filter(
            slug=self.cleaned_data['username'])
        if organization.exists():
            raise forms.ValidationError(
                "A profile with that username already exists.")

        return self.cleaned_data['username']
예제 #7
0
def get_current_broker():
    """
    Returns the provider ``Organization`` as read in the active database
    for the ``djaodjin-saas`` application.
    """
    # If we don't write the code as such, we might end-up generating
    # an extra SQL query every time ``get_current_broker`` is called.
    thread_local_site = get_current_site()
    if not thread_local_site:
        LOGGER.warning(
            "bypassing multitier and returning '%s' as broker, most likely"
            " because the execution environment is not bound to an HTTP"\
            " request.", settings.APP_NAME)
        return get_organization_model().objects.get(slug=settings.APP_NAME)
    broker = getattr(thread_local_site, 'broker', None)
    if not broker:
        # rules.App and saas.Organization are in the same database.
        thread_local_site.broker = get_current_app().account
        broker = thread_local_site.broker
    return broker
예제 #8
0
def inject_edition_tools(response,
                         request,
                         context=None,
                         body_top_template_name=None,
                         body_bottom_template_name=None):
    """
    If the ``request.user`` has editable permissions, this method
    injects the edition tools into the html *content* and return
    a BeautifulSoup object of the resulting content + tools.

    If the response is editable according to the proxy rules, this
    method returns a BeautifulSoup object of the content such that
    ``PageMixin`` inserts the edited page elements.
    """
    #pylint:disable=too-many-locals,too-many-nested-blocks,too-many-statements
    content_type = response.get('content-type', '')
    if not content_type.startswith('text/html'):
        return None

    if not is_authenticated(request):
        return None

    if context is None:
        context = {}

    # ``app.account`` is guarenteed to be in the same database as ``app``.
    # ``site.account`` is always in the *default* database, which is not
    # the expected database ``Organization`` are typically queried from.
    app = get_current_app()
    provider = app.account
    soup = None
    if app.show_edit_tools and get_role_model().objects.valid_for(
            organization=provider, user=request.user):
        edit_urls = {
            'api_medias':
            reverse('uploaded_media_elements', kwargs={'path': ''}),
            'api_sitecss':
            reverse('edit_sitecss'),
            'api_less_overrides':
            reverse('pages_api_less_overrides'),
            'api_sources':
            reverse('pages_api_sources'),
            'api_page_element_base':
            reverse('api_page_element', kwargs={'path': ''}),
            'api_plans':
            reverse('saas_api_plans', args=(provider, )),
            'plan_update_base':
            reverse('saas_plan_base', args=(provider, ))
        }
        try:
            # The following statement will raise an Exception
            # when we are dealing with a ``FileSystemStorage``.
            _ = get_storage_class().bucket_name
            edit_urls.update(
                {'media_upload': reverse('api_credentials_organization')})
        except AttributeError:
            LOGGER.debug("doesn't look like we have a S3Storage.")
        # XXX sites which are hosted on a same domain shouldn't disable
        # all edit functionality, just the edition of base templates.
        site = get_current_site()
        enable_code_editor = is_domain_site(site)
        if enable_code_editor:
            dj_urls = djaoapp_urls(request,
                                   account=provider,
                                   base=site.as_base())
            body_bottom_template_name = "pages/_body_bottom_edit_tools.html"
        else:
            dj_urls = djaoapp_urls(request, account=provider)
            body_bottom_template_name = "pages/_body_bottom.html"

        context.update({
            'ENABLE_CODE_EDITOR': enable_code_editor,
            'FEATURE_DEBUG': settings.FEATURES_DEBUG,
            'urls': {
                'djaodjin': dj_urls,
                'edit': edit_urls
            }
        })
        context.update(csrf(request))
        soup = pages_inject_edition_tools(
            response,
            request,
            context=context,
            body_top_template_name=body_top_template_name,
            body_bottom_template_name=body_bottom_template_name)

    # Insert the authenticated user information and roles on organization.
    if not soup:
        soup = BeautifulSoup(response.content, 'html5lib')
    if soup and soup.body:
        # Implementation Note: we have to use ``.body.next`` here
        # because html5lib "fixes" our HTML by adding missing
        # html/body tags. Furthermore if we use
        #``soup.body.insert(1, BeautifulSoup(body_top, 'html.parser'))``
        # instead, later on ``soup.find_all(class_=...)`` returns
        # an empty set though ``soup.prettify()`` outputs the full
        # expected HTML text.
        auth_user = soup.body.find(class_='header-menubar')
        user_menu_template = '_menubar.html'
        if auth_user and user_menu_template:
            serializer_class = import_string(rules_settings.SESSION_SERIALIZER)
            serializer = serializer_class(request)
            path_parts = reversed(request.path.split('/'))
            top_accessibles = []
            has_broker_role = False
            active_organization = None
            for role, organizations in six.iteritems(serializer.data['roles']):
                for organization in organizations:
                    if organization['slug'] == request.user.username:
                        # Personal Organization
                        continue
                    db_obj = get_organization_model().objects.get(
                        slug=organization['slug'])  # XXX Remove query.
                    if db_obj.is_provider:
                        settings_location = reverse(
                            'saas_dashboard', args=(organization['slug'], ))
                    else:
                        settings_location = reverse(
                            'saas_organization_profile',
                            args=(organization['slug'], ))
                    app_location = reverse('organization_app',
                                           args=(organization['slug'], ))
                    if organization['slug'] in path_parts:
                        active_organization = TopAccessibleOrganization(
                            organization['slug'],
                            organization['printable_name'], settings_location,
                            role, app_location)
                    if is_broker(organization['slug']):
                        has_broker_role = True
                    top_accessibles += [
                        TopAccessibleOrganization(
                            organization['slug'],
                            organization['printable_name'], settings_location,
                            role, app_location)
                    ]
            if not active_organization and has_broker_role:
                active_organization = get_broker()
            context.update({'active_organization': active_organization})
            context.update({'top_accessibles': top_accessibles})
            template = loader.get_template(user_menu_template)
            user_menu = render_template(template, context, request).strip()
            auth_user.clear()
            els = BeautifulSoup(user_menu, 'html5lib').body.ul.children
            for elem in els:
                auth_user.append(BeautifulSoup(str(elem), 'html5lib'))

    return soup
예제 #9
0
class Command(BaseCommand):
    """
    Load the database with random transactions (testing purposes).
    """

    USE_OF_SERVICE = 0
    PAY_BALANCE = 1
    REDEEM = 2
    REFUND = 3
    CHARGEBACK = 4
    WRITEOFF = 5

    FIRST_NAMES = (
        'Anthony',
        'Alexander',
        'Alexis',
        'Alicia',
        'Ashley',
        'Benjamin',
        'Bruce',
        'Chloe',
        'Christopher',
        'Daniel',
        'David',
        'Edward',
        'Emily',
        'Emma',
        'Ethan',
        'Grace',
        'Isabella',
        'Jacob',
        'James',
        'Jayden',
        'Jennifer',
        'John',
        'Julia',
        'Lily',
        'Lucie',
        'Luis',
        'Matthew',
        'Michael',
        'Olivia',
        'Ryan',
        'Samantha',
        'Samuel',
        'Scott',
        'Sophia',
        'Williom',
    )

    LAST_NAMES = (
        'Smith',
        'Johnson',
        'Williams',
        'Jones',
        'Brown',
        'Davis',
        'Miller',
        'Wilson',
        'Moore',
        'Taylor',
        'Anderson',
        'Thomas',
        'Jackson',
        'White',
        'Harris',
        'Martin',
        'Thompson',
        'Garcia',
        'Martinez',
        'Robinson',
        'Clark',
        'Rogriguez',
        'Lewis',
        'Lee',
        'Walker',
        'Hall',
        'Allen',
        'Young',
        'Hernandez',
        'King',
        'Wright',
        'Lopez',
        'Hill',
        'Green',
        'Baker',
        'Gonzalez',
        'Nelson',
        'Mitchell',
        'Perez',
        'Roberts',
        'Turner',
        'Philips',
        'Campbell',
        'Parker',
        'Collins',
        'Stewart',
        'Sanchez',
        'Morris',
        'Rogers',
        'Reed',
        'Cook',
        'Bell',
        'Cooper',
        'Richardson',
        'Cox',
        'Ward',
        'Peterson',
    )

    organization_model = get_organization_model()

    def add_arguments(self, parser):
        parser.add_argument('--provider',
                            action='store',
                            dest='provider',
                            default=settings.SAAS['BROKER']['GET_INSTANCE'],
                            help='create sample subscribers on this provider')

    def handle(self, *args, **options):
        #pylint: disable=too-many-locals,too-many-statements
        RazorpayBackend.bypass_api = True

        now = datetime.datetime.utcnow().replace(tzinfo=utc)
        from_date = now
        from_date = datetime.datetime(year=from_date.year,
                                      month=from_date.month,
                                      day=1)
        if args:
            from_date = datetime.datetime.strptime(args[0], '%Y-%m-%d')
        # Create a set of 3 plans
        broker = get_broker()
        plan, _ = Plan.objects.get_or_create(slug='basic',
                                             defaults={
                                                 'title': "Basic",
                                                 'description': "Basic Plan",
                                                 'period_amount': 24900,
                                                 'broker_fee_percent': 0,
                                                 'period_type': 4,
                                                 'organization': broker,
                                                 'is_active': True
                                             })
        advance_discount = AdvanceDiscount.objects.get_or_create(
            plan=plan,
            discount_type=AdvanceDiscount.PERCENTAGE,
            discount_value=1000,
            length=12)
        Plan.objects.get_or_create(slug='medium',
                                   defaults={
                                       'title': "Medium",
                                       'description': "Medium Plan",
                                       'period_amount': 24900,
                                       'broker_fee_percent': 0,
                                       'period_type': 4,
                                       'organization': broker,
                                       'is_active': True
                                   })
        plan, _ = Plan.objects.get_or_create(slug='premium',
                                             defaults={
                                                 'title': "Premium",
                                                 'description': "Premium Plan",
                                                 'period_amount': 18900,
                                                 'broker_fee_percent': 0,
                                                 'period_type': 4,
                                                 'organization': broker,
                                                 'is_active': True
                                             })
        advance_discount = AdvanceDiscount.objects.get_or_create(
            plan=plan,
            discount_type=AdvanceDiscount.PERCENTAGE,
            discount_value=81,
            length=12)
        # Create Income transactions that represents a growing bussiness.
        provider = self.organization_model.objects.get(
            slug=options['provider'])
        processor = self.organization_model.objects.get(pk=PROCESSOR_ID)
        for end_period in month_periods(from_date=from_date):
            nb_new_customers = random.randint(0, 9)
            for _ in range(nb_new_customers):
                queryset = Plan.objects.filter(organization=provider,
                                               period_amount__gt=0)
                plan = queryset[random.randint(0, queryset.count() - 1)]
                created = False
                trials = 0
                while not created:
                    try:
                        first_name = self.FIRST_NAMES[random.randint(
                            0,
                            len(self.FIRST_NAMES) - 1)]
                        last_name = self.LAST_NAMES[random.randint(
                            0,
                            len(self.LAST_NAMES) - 1)]
                        full_name = '%s %s' % (first_name, last_name)
                        slug = slugify('demo%d' % random.randint(1, 1000))
                        customer, created = \
                            self.organization_model.objects.get_or_create(
                                slug=slug, full_name=full_name)
                    #pylint: disable=catching-non-exception
                    except IntegrityError:
                        trials = trials + 1
                        if trials > 10:
                            raise RuntimeError(
                                'impossible to create a new customer after 10 trials.'
                            )
                self.organization_model.objects.filter(pk=customer.id).update(
                    created_at=end_period)
                subscription = Subscription.objects.create(
                    organization=customer,
                    plan=plan,
                    ends_at=now + datetime.timedelta(days=31))
                Subscription.objects.filter(pk=subscription.id).update(
                    created_at=end_period)
            # Insert some churn in %
            churn_rate = 2
            all_subscriptions = Subscription.objects.filter(
                plan__organization=provider)
            nb_churn_customers = (all_subscriptions.count() * churn_rate //
                                  100)
            subscriptions = random.sample(
                list(all_subscriptions),
                all_subscriptions.count() - nb_churn_customers)
            for subscription in subscriptions:
                nb_periods = random.randint(1, 6)
                amount = nb_periods * subscription.plan.period_amount
                ends_at = subscription.plan.end_of_period(
                    subscription.ends_at, nb_periods)
                transaction_item = Transaction.objects.new_subscription_order(
                    subscription,
                    amount=amount,
                    descr=humanize.describe_buy_periods(
                        subscription.plan, ends_at, nb_periods),
                    created_at=end_period)
                if transaction_item.dest_amount < 50:
                    continue
                transaction_item.orig_amount = transaction_item.dest_amount
                transaction_item.orig_unit = transaction_item.dest_unit
                transaction_item.save()
                charge = Charge.objects.create(
                    created_at=transaction_item.created_at,
                    amount=transaction_item.dest_amount,
                    customer=subscription.organization,
                    description='Charge for %d periods' % nb_periods,
                    processor=processor,
                    processor_key=str(transaction_item.pk),
                    # XXX We can't do that yet because of
                    # ``PROCESSOR_BACKEND.charge_distribution(self)``
                    #                    unit=transaction_item.dest_unit,
                    state=Charge.CREATED)
                charge.created_at = transaction_item.created_at
                charge.save()
                ChargeItem.objects.create(invoiced=transaction_item,
                                          charge=charge)
                charge.payment_successful(receipt_info={
                    'last4': 1241,
                    'exp_date': datetime_or_now()
                })
            churned = all_subscriptions.exclude(
                pk__in=[subscription.pk for subscription in subscriptions])
            for subscription in churned:
                subscription.ends_at = end_period
                subscription.save()
            self.stdout.write(
                "%d new and %d churned customers at %s" %
                (nb_new_customers, nb_churn_customers, end_period))
예제 #10
0
class OrganizationListView(ListView):

    model = get_organization_model()
    template_name = 'organization_list_index.html'