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()
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)
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
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
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)
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']
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
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
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))
class OrganizationListView(ListView): model = get_organization_model() template_name = 'organization_list_index.html'