def render_js_extra(context, **pageargs):
    __M_caller = context.caller_stack._push_frame()
    try:

        def js_extra():
            return render_js_extra(context)

        settings = context.get("settings", UNDEFINED)
        __M_writer = context.writer()
        # SOURCE LINE 57
        __M_writer(
            u"\n  <script type=\"text/javascript\">\n    $(function() {\n\n      var view_name = 'view-login';\n\n      // adding js class for styling with accessibility in mind\n      $('body').addClass('js').addClass(view_name);\n\n      // new window/tab opening\n      $('a[rel=\"external\"], a[class=\"new-vp\"]')\n      .click( function() {\n      window.open( $(this).attr('href') );\n      return false;\n      });\n\n      // form field label styling on focus\n      $(\"form :input\").focus(function() {\n        $(\"label[for='\" + this.id + \"']\").parent().addClass(\"is-focused\");\n      }).blur(function() {\n        $(\"label\").parent().removeClass(\"is-focused\");\n      });\n    });\n\n    (function() {\n        var urlParams = {};\n        var searchString = window.location.search.substring(1);\n        if (searchString !== undefined) {\n            var pairs = searchString.split('&');\n            for (var i = 0; i < pairs.length; i++) {\n                var values = pairs[i].split('=');\n                urlParams[values[0]] = values[1] === undefined ? false : decodeURIComponent(values[1]);\n            }\n        }\n\n        if (localStorage.pepperpd_login_email)\n        {\n            $(\"#email\").val(localStorage.pepperpd_login_email);\n        }\n        toggleSubmitButton(true);\n\n        $('#login-form').on('submit', function() {\n            toggleSubmitButton(false);\n        });\n\n        $('#login-form').on('ajax:error', function() {\n            toggleSubmitButton(true);\n        });\n\n        $('#login-form').on('ajax:success', function(event, json, xhr) {\n            if (json.success) {\n                if ($('#remember-yes').attr(\"checked\"))\n                {\n                    localStorage.pepperpd_login_email=$(\"#email\").val();\n                }\n\n                if (urlParams['next'] !== undefined && !isExternal(urlParams['next']) && urlParams['next'].match(/^course.+courseware/)) {\n                    var next = urlParams['next'];\n                    if (next.substring(0,1) != '/') {\n                        next = '/' + next;\n                    }\n                  location.href = next;\n                } else if (urlParams['next'] !== undefined && !isExternal(urlParams['next']) && urlParams['next'].match(/^\\/sso\\/idp\\/auth/)) {\n                   location.href = urlParams['next'];\n                }  else {\n                    location.href = \""
        )
        # SOURCE LINE 122
        __M_writer(filters.decode.utf8(reverse("dashboard")))
        __M_writer(
            u"\";\n                }\n\n            } else {\n                toggleSubmitButton(true);\n                $('.message.submission-error').addClass('is-shown').focus();\n                $('.message.submission-error .message-copy').html(json.value);\n            }\n        });\n    })(this);\n\n    function toggleSubmitButton(enable) {\n      var $submitButton = $('form .form-actions #submit');\n\n\n      if(enable) {\n        $submitButton.\n          removeClass('is-disabled').\n          removeProp('disabled').\n\n          <!--@begin:modify LogIn -> Log In-->\n          <!--@date:2013-11-02-->\n          html(\""
        )
        # SOURCE LINE 144
        __M_writer(filters.decode.utf8(_("Log In").format(platform_name=settings.PLATFORM_NAME)))
        __M_writer(u" <span class='orn-plus'>&</span> ")
        __M_writer(filters.decode.utf8(_("Access My Courses")))
        __M_writer(
            u"\");\n          <!--@end-->\n      } else {\n        $submitButton.\n          addClass('is-disabled').\n          prop('disabled', true).\n          html(gettext('Processing your account information &hellip;'));\n      }\n\n    }\n\n//------------- Clear time storage-------------------//\n\n    function clearTimeSessionStorage(){\n      var user_id = sessionStorage.getItem('user_id');\n      if(user_id != null){\n        sessionStorage.removeItem(user_id + '_course_id');\n        sessionStorage.removeItem(user_id + '_vertical_id');\n        sessionStorage.removeItem(user_id + '_type');\n        sessionStorage.removeItem(user_id + '_time');\n        sessionStorage.removeItem('user_id');\n      }\n    }\n\n    clearTimeSessionStorage();\n\n  </script>\n"
        )
        return ""
    finally:
        context.caller_stack._pop_frame()
Example #2
0
def create_saved_search(request, database_name=settings.MONGO_DB_NAME,
                collection_name=settings.MONGO_MASTER_COLLECTION,
                        skip=0, limit=200, return_keys=()):
    name = _("Create a Saved Search")
    if request.method == 'POST':
        form = SavedSearchForm(request.POST)
        if form.is_valid():
            ss = form.save(commit = False)
            ss.user = request.user
            ss.save()
                
            return HttpResponseRedirect(reverse('saved_searches'))
        else:
            #The form is invalid
             messages.error(request,_("Please correct the errors in the form."))
             return render_to_response('generic/bootstrapform.html',
                                            {'form': form,
                                             'name':name,
                                             },
                                            RequestContext(request))
            
   #this is a GET
    idata ={'database_name': database_name,
            'collection_name': collection_name}
    
    
    context= {'name':name,
              'form': SavedSearchForm(initial=idata)
              }
    return render_to_response('generic/bootstrapform.html',
                             RequestContext(request, context,))
Example #3
0
 def handle(self, *args, **options):
     message = options.get('message')
     verbosity = options.get('verbosity')
     get_template('templated_email/notice.email')
     if not message:
         raise CommandError(_('message must be included in options'))
     # get all users
     users = User.objects.all()
     for user in users:
         name = user.get_full_name()
         if not name or len(name) == 0:
             name = user.email
         if verbosity:
             self.stdout.write(_(
                 'Emailing name: %(name)s, email: %(email)s')
                 % {'name': name, 'email': user.email})
         # send each email separately so users cannot see eachother
         send_templated_mail(
             template_name='notice',
             from_email='*****@*****.**',
             recipient_list=[user.email],
             context={
                 'username': user.username,
                 'full_name': name,
                 'message': message
             }, )
Example #4
0
File: files.py Project: nirgal/ngw
 def form_valid(self, form):
     cg = self.contactgroup
     request = self.request
     if not cg.userperms & perms.WRITE_FILES:
         raise PermissionDenied
     upfile = request.FILES['file_to_upload']
     # name has already been sanitized by UploadedFile._set_name
     path = self.kwargs['path']
     fullfilename = cg.get_fullfilename(os.path.join(path, upfile.name))
     destination = None
     try:  # We're not using "with" because we want to show errors
         destination = open(force_str(fullfilename), 'wb')
         for chunk in upfile.chunks():
             destination.write(chunk)
         messages.add_message(
             request, messages.SUCCESS,
             _('File {} has been uploaded successfully.')
             .format(upfile.name))
     except IOError as err:
         messages.add_message(
             request, messages.ERROR,
             _('Could not upload file {filename}: {error}').format(
                 filename=upfile.name,
                 error=err))
     finally:
         if destination:
             destination.close()
     return self.get(self.request)
Example #5
0
    def clean(self):
        self.handle_deleted_and_added_contributions()

        super().clean()

        found_contributor = set()
        count_responsible = 0
        for form in self.forms:
            if not form.cleaned_data or form.cleaned_data.get('DELETE'):
                continue
            contributor = form.cleaned_data.get('contributor')
            if contributor is None:
                raise forms.ValidationError(_('Please select the name of each added contributor. Remove empty rows if necessary.'))
            if contributor and contributor in found_contributor:
                raise forms.ValidationError(_('Duplicate contributor found. Each contributor should only be used once.'))
            elif contributor:
                found_contributor.add(contributor)

            if form.cleaned_data.get('responsibility') == 'RESPONSIBLE':
                count_responsible += 1

        if count_responsible < 1:
            raise forms.ValidationError(_('No responsible contributor found. Each course must have exactly one responsible contributor.'))
        elif count_responsible > 1:
            raise forms.ValidationError(_('Too many responsible contributors found. Each course must have exactly one responsible contributor.'))
Example #6
0
    def crop_and_upload_image_to_popit(self, image_filename, crop_bounds, moderator_why_allowed, make_primary):
        original = PillowImage.open(image_filename)
        # Some uploaded images are CYMK, which gives you an error when
        # you try to write them as PNG, so convert to RGBA (this is
        # RGBA rather than RGB so that any alpha channel (transparency)
        # is preserved).
        person_id = self.queued_image.person.id
        person_extra = PersonExtra.objects.get(base__id=person_id)
        original = original.convert('RGBA')
        cropped = original.crop(crop_bounds)
        ntf = NamedTemporaryFile(delete=False)
        cropped.save(ntf.name, 'PNG')
        md5sum = get_file_md5sum(ntf.name)
        filename = str(person_id) + '.png'
        source = _(
            'Uploaded by {uploaded_by}: Approved from photo moderation queue'
        ).format(uploaded_by=self.queued_image.user.username)

        ImageExtra.objects.create_from_file(
            ntf.name,
            join('images', filename),
            base_kwargs={
                'source': source,
                'is_primary': make_primary,
                'content_object': person_extra,
            },
            extra_kwargs={
                'md5sum': md5sum,
                'uploading_user': self.queued_image.user,
                'user_notes': self.queued_image.justification_for_use,
                'copyright': moderator_why_allowed,
                'user_copyright': self.queued_image.why_allowed,
                'notes': _('Approved from photo moderation queue'),
            },
        )
Example #7
0
    def get_data(self):
        # Gather our instances
        try:
            instances = api.server_list(self.request)
        except:
            instances = []
            exceptions.handle(self.request,
                              _('Unable to retrieve instances.'))
        # Gather our flavors and correlate our instances to them
        if instances:
            try:
                flavors = api.flavor_list(self.request)
            except:
                flavors = []
                exceptions.handle(self.request, ignore=True)

            full_flavors = SortedDict([(str(flavor.id), flavor)
                                        for flavor in flavors])
            # Loop through instances to get flavor info.
            for instance in instances:
                try:
                    flavor_id = instance.flavor["id"]
                    if flavor_id in full_flavors:
                        instance.full_flavor = full_flavors[flavor_id]
                    else:
                        # If the flavor_id is not in full_flavors list,
                        # get it via nova api.
                        instance.full_flavor = api.flavor_get(self.request,
                                                              flavor_id)
                except:
                    msg = _('Unable to retrieve instance size information.')
                    exceptions.handle(self.request, msg)
        return instances
Example #8
0
def dump_student_extensions(course, student):
    """
    Dumps data about the due date extensions granted for a particular student
    in a particular course.
    """
    data = []
    header = [_("Unit"), _("Extended Due Date")]
    units = get_units_with_due_date(course)
    units = {u.location: u for u in units}
    query = StudentFieldOverride.objects.filter(
        course_id=course.id,
        student=student,
        field='due')
    for override in query:
        location = override.location.replace(course_key=course.id)
        if location not in units:
            continue
        due = DATE_FIELD.from_json(json.loads(override.value))
        due = due.strftime("%Y-%m-%d %H:%M")
        title = title_or_url(units[location])
        data.append(dict(zip(header, (title, due))))
    return {
        "header": header,
        "title": _("Due date extensions for {0} {1} ({2})").format(
            student.first_name, student.last_name, student.username),
        "data": data}
Example #9
0
 def handle(self, request, data):
     domain = api.keystone.get_default_domain(self.request)
     try:
         LOG.info('Creating user with name "%s"' % data['name'])
         if "email" in data:
             data['email'] = data['email'] or None
         new_user = api.keystone.user_create(request,
                                             name=data['name'],
                                             email=data['email'],
                                             password=data['password'],
                                             project=data['project'],
                                             enabled=True,
                                             domain=domain.id)
         messages.success(request,
                          _('User "%s" was successfully created.')
                          % data['name'])
         if data['project'] and data['role_id']:
             roles = api.keystone.roles_for_user(request,
                                     new_user.id,
                                     data['project']) or []
             assigned = [role for role in roles if role.id == str(
                 data['role_id'])]
             if not assigned:
                 try:
                     api.keystone.add_tenant_user_role(request,
                                                     data['project'],
                                                     new_user.id,
                                                     data['role_id'])
                 except Exception:
                     exceptions.handle(request,
                                     _('Unable to add user '
                                         'to primary project.'))
         return new_user
     except Exception:
         exceptions.handle(request, _('Unable to create user.'))
Example #10
0
File: api.py Project: heshunwq/hue
def export_result(request):
  response = {'status': -1, 'message': _('Exporting result failed.')}

  # Passed by check_document_access_permission but unused by APIs
  notebook = json.loads(request.POST.get('notebook', '{}'))
  snippet = json.loads(request.POST.get('snippet', '{}'))
  data_format = json.loads(request.POST.get('format', 'hdfs-file'))
  destination = json.loads(request.POST.get('destination', ''))
  overwrite = json.loads(request.POST.get('overwrite', False))

  api = get_api(request, snippet)

  if data_format == 'hdfs-file':
    if overwrite and request.fs.exists(destination):
      if request.fs.isfile(destination):
        request.fs.do_as_user(request.user.username, request.fs.rmtree, destination)
      else:
        raise ValidationError(_("The target path is a directory"))
    response['watch_url'] = api.export_data_as_hdfs_file(snippet, destination, overwrite)
    response['status'] = 0
  elif data_format == 'hive-table':
    notebook_id = notebook['id'] or request.GET.get('editor', request.GET.get('notebook'))
    response['watch_url'] = reverse('notebook:execute_and_watch') + '?action=save_as_table&notebook=' + str(notebook_id) + '&snippet=0&destination=' + destination
    response['status'] = 0
  elif data_format == 'hdfs-directory':
    notebook_id = notebook['id'] or request.GET.get('editor', request.GET.get('notebook'))
    response['watch_url'] = reverse('notebook:execute_and_watch') + '?action=insert_as_query&notebook=' + str(notebook_id) + '&snippet=0&destination=' + destination
    response['status'] = 0

  return JsonResponse(response)
Example #11
0
def dump_module_extensions(course, unit):
    """
    Dumps data about students with due date extensions for a particular module,
    specified by 'url', in a particular course.
    """
    data = []
    header = [_("Username"), _("Full Name"), _("Extended Due Date")]
    query = StudentFieldOverride.objects.filter(
        course_id=course.id,
        location=unit.location,
        field='due')
    for override in query:
        due = DATE_FIELD.from_json(json.loads(override.value))
        due = due.strftime("%Y-%m-%d %H:%M")
        fullname = override.student.profile.name
        data.append(dict(zip(
            header,
            (override.student.username, fullname, due))))
    data.sort(key=lambda x: x[header[0]])
    return {
        "header": header,
        "title": _("Users with due date extensions for {0}").format(
            title_or_url(unit)),
        "data": data
    }
Example #12
0
 def clean_shipping_percentage(self):
     if self.value > 100:
         raise ValidationError(_("Percentage discount cannot be greater than 100"))
     if self.range:
         raise ValidationError(_("No range should be selected as this benefit does not " "apply to products"))
     if self.max_affected_items:
         raise ValidationError(_("Shipping discounts don't require a 'max affected items' " "attribute"))
Example #13
0
def merge_path(request):
    """
    Path merging view
    """
    response = {}

    if request.method == 'POST':
        try:
            ids_path_merge = request.POST.getlist('path[]')

            if len(ids_path_merge) == 2:
                path_a = Path.objects.get(pk=ids_path_merge[0])
                path_b = Path.objects.get(pk=ids_path_merge[1])
                if not path_a.same_structure(request.user) or not path_b.same_structure(request.user):
                    response = {'error': _(u"You don't have the right to change these paths")}

                elif path_a.merge_path(path_b):
                    response = {'success': _(u"Paths merged successfully")}
                    messages.success(request, _(u"Paths merged successfully"))

                else:
                    response = {'error': _(u"No matching points to merge paths found")}

            else:
                raise

        except Exception as exc:
            response = {'error': exc, }

    return HttpResponse(json.dumps(response), mimetype="application/json")
Example #14
0
 def clean_multibuy(self):
     if not self.range:
         raise ValidationError(_("Multibuy benefits require a product range"))
     if self.value:
         raise ValidationError(_("Multibuy benefits don't require a value"))
     if self.max_affected_items:
         raise ValidationError(_("Multibuy benefits don't require a 'max affected items' " "attribute"))
Example #15
0
 def clean_shipping_absolute(self):
     if not self.value:
         raise ValidationError(_("A discount value is required"))
     if self.range:
         raise ValidationError(_("No range should be selected as this benefit does not " "apply to products"))
     if self.max_affected_items:
         raise ValidationError(_("Shipping discounts don't require a 'max affected items' " "attribute"))
Example #16
0
    def clean_destination(self):
        create = self.cleaned_data.get("create")
        dest = self.cleaned_data.get("destination")
        dest = os.path.abspath(dest.strip().replace("..", ""))

        if not self.jail:
            jail = self.cleaned_data.get("jail")
            if jail:
                self.jail = Jails.objects.get(jail_host=jail)

        if not self.jail:
            raise forms.ValidationError(
                _("This shouldn't happen, but the jail could not be found")
            )

        full = "%s/%s%s" % (self.jc.jc_path, self.jail.jail_host, dest)

        if len(full) > 88:
            raise forms.ValidationError(
                _("The full path cannot exceed 88 characters")
            )

        if not os.path.exists(full):
            os.makedirs(full)

        return dest
Example #17
0
    def get_visibility_errors(self, customer):
        if self.product.deleted:
            yield ValidationError(_('This product has been deleted.'), code="product_deleted")

        if customer and customer.is_all_seeing:  # None of the further conditions matter for omniscient customers.
            return

        if not self.visible:
            yield ValidationError(_('This product is not visible.'), code="product_not_visible")

        is_logged_in = (bool(customer) and not customer.is_anonymous)

        if not is_logged_in and self.visibility_limit != ProductVisibility.VISIBLE_TO_ALL:
            yield ValidationError(
                _('The Product is invisible to users not logged in.'),
                code="product_not_visible_to_anonymous")

        if is_logged_in and self.visibility_limit == ProductVisibility.VISIBLE_TO_GROUPS:
            # TODO: Optimization
            user_groups = set(customer.groups.all().values_list("pk", flat=True))
            my_groups = set(self.visibility_groups.values_list("pk", flat=True))
            if not bool(user_groups & my_groups):
                yield ValidationError(
                    _('This product is not visible to your group.'),
                    code="product_not_visible_to_group"
                )

        for receiver, response in get_visibility_errors.send(ShopProduct, shop_product=self, customer=customer):
            for error in response:
                yield error
Example #18
0
 def check_server_disallowed(self):
     """
     Check if server domain name or IP is disallowed in settings.py.
     """
     hostname = self.netloc_parts[2].lower()
     if (hasattr(settings, 'DISALLOWED_DOMAIN_LIST') and
         settings.DISALLOWED_DOMAIN_LIST):
         for domain in settings.DISALLOWED_DOMAIN_LIST:
             if hostname == domain or hostname.endswith('.' + domain):
                 raise ValidationError(unicode(
                     _("Domain name %(domain)s is disallowed.") % locals()))
     try:
         ip = socket.gethostbyname(hostname)
     except socket.error:
         raise ValidationError(unicode(
             _("Could not resolve IP address for %(hostname)s.") %
             locals()))
     if (not hasattr(settings, 'DISALLOWED_SERVER_IP_LIST') or
         not settings.DISALLOWED_SERVER_IP_LIST):
         return
     server = long_ip(ip)
     # print 'server', server, dotted_ip(server), ip
     for disallowed in settings.DISALLOWED_SERVER_IP_LIST:
         disallowed = disallowed.strip()
         if disallowed == '' or disallowed.startswith('#'):
             continue
         mask = bit_mask(32)
         if '/' in disallowed:
             disallowed, bits = disallowed.split('/', 1)
             mask = slash_mask(int(bits))
         identifier = long_ip(disallowed) & mask
         masked = server & mask
         if masked == identifier:
             raise ValidationError(unicode(
                 _("Server IP address %(ip)s is disallowed.") % locals()))
Example #19
0
    def __init__(self, *args, **kwargs):
        super(ReservationCrispyFormModelForm,self).__init__(*args, **kwargs)

        self.helper = FormHelper(self)
        self.helper.form_action = ""
        self.helper.form_method = "POST"
        self.helper.form_class = 'form-horizontal'
        self.helper.label_class = 'col-sm-3'
        self.helper.field_class = 'col-sm-4'
        self.fields["acceptation"].widget = forms.RadioSelect()
        # delete empty choice for the type
        del self.fields["acceptation"].choices[0]
        self.helper.layout = layout.Layout(
            layout.Fieldset(
                _("Main data"),
                Field("acceptation", css_class="input-block-level"),
                _("Renseignement"),
                Field("firstname", css_class="input-block-level"),
                Field("lastname", css_class="input-block-level"),
                Field("telephone", css_class="input-block-level"),
                Field("accompagnement", css_class="input-block-level"),
                Field("notes", css_class="input-block-level", rows="3"),
                ),
                FormActions(
                    Submit("submit", _("Save")),
                    Button('cancel', 'Cancelar', onclick="window.location.href='/';")
                )
        )
 def __init__(self, *args, **kwargs):
     super(SummaryField, self).__init__(*args, **kwargs)
     self.required = False
     self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
     self.max_length = 300
     self.label  = _('update summary:')
     self.help_text = _('enter a brief summary of your revision (e.g. fixed spelling, grammar, improved style, this field is optional)')
Example #21
0
def get_size(instance):
    if hasattr(instance, "full_flavor"):
        size_string = _("%(name)s | %(RAM)s RAM")
        vals = {'name': instance.full_flavor.name,
                'RAM': sizeformat.mbformat(instance.full_flavor.ram)}
        return size_string % vals
    return _("Not available")
Example #22
0
    def __init__(self, *args, **kwargs):
        from servo.lib.utils import empty
        super(GsxRepairForm, self).__init__(*args, **kwargs)
        repair = self.instance
        techs = User.techies.filter(location=repair.order.location)
        c = [(u.tech_id, u.get_full_name()) for u in techs]
        c.insert(0, ('', '-------------------',))
        self.fields['tech_id'] = forms.ChoiceField(choices=c,
                                                   required=False,
                                                   label=_('Technician'))
        self.fields['parts'].initial = repair.order.get_parts()

        if repair.can_mark_complete is False:
            del(self.fields['mark_complete'])
            del(self.fields['replacement_sn'])

        choices = Template.templates()
        for f in ('notes', 'symptom', 'diagnosis',):
            self.fields[f].widget = AutocompleteTextarea(choices=choices)
        
        symptom_codes = self.instance.get_symptom_code_choices()
        self.fields['symptom_code'] = forms.ChoiceField(choices=symptom_codes,
                                                        label=_('Symptom group'))
        
        if empty(self.instance.symptom_code):
            # default to the first choice
            self.instance.symptom_code = symptom_codes[0][0]

        issue_codes = self.instance.get_issue_code_choices()
        self.fields['issue_code'] = forms.ChoiceField(choices=issue_codes,
                                                      label=_('Issue code'))
Example #23
0
    def confirm(self, ok_func, *msgs):
        """Execute the specified callable `ok_func` after the user has
        confirmed the specified message.
        
        The confirmation message may be specified as a series of
        positional arguments which will be concatenated to a single
        prompt.

        The callable will be called with a single positional argument
        which will be the action request that confirmed the
        message. In a web context this will be another object than
        this one.

        In a non-interactive environment the `ok_func` function is
        called directly (i.e. we don't ask any confirmation and act as
        confirmation had been given).

        """
        cb = self.add_callback(*msgs)

        def noop(ar):
            return ar.success(_("Aborted"))
        cb.add_choice('yes', ok_func, _("Yes"))
        cb.add_choice('no', noop, _("No"))
        self.set_callback(cb)

        if not self.renderer.is_interactive:
            if self._confirm_answer:
                ok_func(self)
Example #24
0
 def get_human_hour(self):
     if self.scrub_hour == '*':
         return _(u'Every hour')
     elif self.scrub_hour.startswith('*/'):
         return _(u'Every {0} hour(s)').format(self.scrub_hour.split('*/')[1])
     else:
         return self.scrub_hour
Example #25
0
 def get_human_daymonth(self):
     if self.scrub_daymonth == '*':
         return _(u'Everyday')
     elif self.scrub_daymonth.startswith('*/'):
         return _(u'Every {0} days').format(self.scrub_daymonth.split('*/')[1])
     else:
         return self.scrub_daymonth
Example #26
0
    def __init__(self, path, raw_file_url=None, username=None, password=None,
                 local_site_name=None):
        if not is_exe_in_path('git'):
            # This is technically not the right kind of error, but it's the
            # pattern we use with all the other tools.
            raise ImportError

        self.path = self._normalize_git_url(path)
        self.raw_file_url = raw_file_url
        self.username = username
        self.password = password
        self.local_site_name = local_site_name
        self.git_dir = None

        url_parts = urlparse.urlparse(self.path)

        if url_parts[0] == 'file':
            self.git_dir = url_parts[2]

            p = self._run_git(['--git-dir=%s' % self.git_dir, 'config',
                               'core.repositoryformatversion'])
            failure = p.wait()

            if failure:
                # See if we have a permissions error
                if not os.access(self.git_dir, os.R_OK):
                    raise SCMError(_("Permission denied accessing the local "
                                     "Git repository '%s'") % self.git_dir)
                else:
                    raise SCMError(_('Unable to retrieve information from '
                                     'local Git repository'))
Example #27
0
 def get_human_minute(self):
     if self.scrub_minute == '*':
         return _(u'Every minute')
     elif self.scrub_minute.startswith('*/'):
         return _(u'Every {0} minute(s)').format(self.scrub_minute.split('*/')[1])
     else:
         return self.scrub_minute
Example #28
0
def select_variant_from_properties(request):
    """
    This is called via an ajax call if the combination of properties are
    changed.
    """
    product_id = request.POST.get("product_id")

    try:
        variant = Product.objects.get(pk=product_id)
    except Product.DoesNotExist:
        return HttpResponse("")
    else:
        product = variant.parent

    options = lfs_utils.parse_properties(request)
    variant = product.get_variant(options)

    if variant is None:
        msg = _(u"The choosen combination of properties is not deliverable.")
        variant = product.get_default_variant()
    else:
        msg = _(u"The product has been changed according to your selection.")

    result = simplejson.dumps({
        "product": product_inline(request, variant),
        "message": msg,
    }, cls=LazyEncoder)

    return HttpResponse(result, mimetype='application/json')
Example #29
0
def video_handler(request, video, format="mp4", prev=None, next=None):

    if not video["available"]:
        if request.is_admin:
            # TODO(bcipolli): add a link, with querystring args that auto-checks this video in the topic tree
            messages.warning(request, _("This video was not found! You can download it by going to the Update page."))
        elif request.is_logged_in:
            messages.warning(request, _("This video was not found! Please contact your teacher or an admin to have it downloaded."))
        elif not request.is_logged_in:
            messages.warning(request, _("This video was not found! You must login as an admin/teacher to download the video."))

    # Fallback mechanism
    available_urls = dict([(lang, avail) for lang, avail in video["availability"].iteritems() if avail["on_disk"]])
    if video["available"] and not available_urls:
        vid_lang = "en"
        messages.success(request, "Got video content from %s" % video["availability"]["en"]["stream"])
    else:
        vid_lang = select_best_available_language(request.language, available_codes=available_urls.keys())


    context = {
        "video": video,
        "title": video["title"],
        "selected_language": vid_lang,
        "video_urls": video["availability"].get(vid_lang),
        "subtitle_urls": video["availability"].get(vid_lang, {}).get("subtitles"),
        "prev": prev,
        "next": next,
        "backup_vids_available": bool(settings.BACKUP_VIDEO_SOURCE),
        "use_mplayer": settings.USE_MPLAYER and is_loopback_connection(request),
    }
    return context
Example #30
0
 def object_action(self, form, instance):
     try:
         if instance.is_superuser or instance.is_staff:
             messages.error(
                 self.request,
                 _(
                     'Super user and staff user password '
                     'reseting is not allowed, use the admin '
                     'interface for these cases.'
                 )
             )
         else:
             instance.set_password(form.cleaned_data['new_password_1'])
             instance.save()
             messages.success(
                 self.request, _(
                     'Successfull password reset for user: %s.'
                 ) % instance
             )
     except Exception as exception:
         messages.error(
             self.request, _(
                 'Error reseting password for user "%(user)s": %(error)s'
             ) % {
                 'user': instance, 'error': exception
             }
         )
Example #31
0
 class Meta:
     abstract = True
     app_label = 'order'
     verbose_name = _("Order Discount")
     verbose_name_plural = _("Order Discounts")
Example #32
0
 def __str__(self):
     return _("'%(type)s' event for order #%(number)s") \
         % {'type': self.event_type.name, 'number': self.order.number}
Example #33
0
 class Meta:
     abstract = True
     app_label = 'order'
     verbose_name = _("Communication Event")
     verbose_name_plural = _("Communication Events")
     ordering = ['-date_created']
Example #34
0
 class Meta:
     abstract = True
     app_label = 'order'
     verbose_name = _("Order Note")
     verbose_name_plural = _("Order Notes")
Example #35
0
 class Meta:
     abstract = True
     app_label = 'order'
     ordering = ['-date_placed']
     verbose_name = _("Order")
     verbose_name_plural = _("Orders")
Example #36
0
class AbstractOrder(models.Model):
    """
    The main order model
    """
    number = models.CharField(_("Order number"),
                              max_length=128,
                              db_index=True,
                              unique=True)

    # We track the site that each order is placed within
    site = models.ForeignKey('sites.Site',
                             verbose_name=_("Site"),
                             null=True,
                             on_delete=models.SET_NULL)

    basket = models.ForeignKey('basket.Basket',
                               verbose_name=_("Basket"),
                               null=True,
                               blank=True,
                               on_delete=models.SET_NULL)

    # Orders can be placed without the user authenticating so we don't always
    # have a customer ID.
    user = models.ForeignKey(AUTH_USER_MODEL,
                             related_name='orders',
                             null=True,
                             blank=True,
                             verbose_name=_("User"),
                             on_delete=models.SET_NULL)

    # Billing address is not always required (eg paying by gift card)
    billing_address = models.ForeignKey('order.BillingAddress',
                                        null=True,
                                        blank=True,
                                        verbose_name=_("Billing Address"),
                                        on_delete=models.SET_NULL)

    # Total price looks like it could be calculated by adding up the
    # prices of the associated lines, but in some circumstances extra
    # order-level charges are added and so we need to store it separately
    currency = models.CharField(_("Currency"),
                                max_length=12,
                                default=get_default_currency)
    total_incl_tax = models.DecimalField(_("Order total (inc. tax)"),
                                         decimal_places=2,
                                         max_digits=12)
    total_excl_tax = models.DecimalField(_("Order total (excl. tax)"),
                                         decimal_places=2,
                                         max_digits=12)

    # Shipping charges
    shipping_incl_tax = models.DecimalField(_("Shipping charge (inc. tax)"),
                                            decimal_places=2,
                                            max_digits=12,
                                            default=0)
    shipping_excl_tax = models.DecimalField(_("Shipping charge (excl. tax)"),
                                            decimal_places=2,
                                            max_digits=12,
                                            default=0)

    # Not all lines are actually shipped (such as downloads), hence shipping
    # address is not mandatory.
    shipping_address = models.ForeignKey('order.ShippingAddress',
                                         null=True,
                                         blank=True,
                                         verbose_name=_("Shipping Address"),
                                         on_delete=models.SET_NULL)
    shipping_method = models.CharField(_("Shipping method"),
                                       max_length=128,
                                       blank=True)

    # Identifies shipping code
    shipping_code = models.CharField(blank=True, max_length=128, default="")

    # Use this field to indicate that an order is on hold / awaiting payment
    status = models.CharField(_("Status"), max_length=100, blank=True)
    guest_email = models.EmailField(_("Guest email address"), blank=True)

    # Index added to this field for reporting
    date_placed = models.DateTimeField(db_index=True)

    #: Order status pipeline.  This should be a dict where each (key, value) #:
    #: corresponds to a status and a list of possible statuses that can follow
    #: that one.
    pipeline = getattr(settings, 'OSCAR_ORDER_STATUS_PIPELINE', {})

    #: Order status cascade pipeline.  This should be a dict where each (key,
    #: value) pair corresponds to an *order* status and the corresponding
    #: *line* status that needs to be set when the order is set to the new
    #: status
    cascade = getattr(settings, 'OSCAR_ORDER_STATUS_CASCADE', {})

    @classmethod
    def all_statuses(cls):
        """
        Return all possible statuses for an order
        """
        return list(cls.pipeline.keys())

    def available_statuses(self):
        """
        Return all possible statuses that this order can move to
        """
        return self.pipeline.get(self.status, ())

    def set_status(self, new_status):
        """
        Set a new status for this order.

        If the requested status is not valid, then ``InvalidOrderStatus`` is
        raised.
        """
        if new_status == self.status:
            return

        old_status = self.status

        if new_status not in self.available_statuses():
            raise exceptions.InvalidOrderStatus(
                _("'%(new_status)s' is not a valid status for order %(number)s"
                  " (current status: '%(status)s')") % {
                      'new_status': new_status,
                      'number': self.number,
                      'status': self.status
                  })
        self.status = new_status
        if new_status in self.cascade:
            for line in self.lines.all():
                line.status = self.cascade[self.status]
                line.save()
        self.save()

        # Send signal for handling status changed
        order_status_changed.send(
            sender=self,
            order=self,
            old_status=old_status,
            new_status=new_status,
        )

    set_status.alters_data = True

    @property
    def is_anonymous(self):
        # It's possible for an order to be placed by a customer who then
        # deletes their profile.  Hence, we need to check that a guest email is
        # set.
        return self.user is None and bool(self.guest_email)

    @property
    def basket_total_before_discounts_incl_tax(self):
        """
        Return basket total including tax but before discounts are applied
        """
        total = D('0.00')
        for line in self.lines.all():
            total += line.line_price_before_discounts_incl_tax
        return total

    @property
    def basket_total_before_discounts_excl_tax(self):
        """
        Return basket total excluding tax but before discounts are applied
        """
        total = D('0.00')
        for line in self.lines.all():
            total += line.line_price_before_discounts_excl_tax
        return total

    @property
    def basket_total_incl_tax(self):
        """
        Return basket total including tax
        """
        return self.total_incl_tax - self.shipping_incl_tax

    @property
    def basket_total_excl_tax(self):
        """
        Return basket total excluding tax
        """
        return self.total_excl_tax - self.shipping_excl_tax

    @property
    def total_before_discounts_incl_tax(self):
        return (self.basket_total_before_discounts_incl_tax +
                self.shipping_incl_tax)

    @property
    def total_before_discounts_excl_tax(self):
        return (self.basket_total_before_discounts_excl_tax +
                self.shipping_excl_tax)

    @property
    def total_discount_incl_tax(self):
        """
        The amount of discount this order received
        """
        discount = D('0.00')
        for line in self.lines.all():
            discount += line.discount_incl_tax
        return discount

    @property
    def total_discount_excl_tax(self):
        discount = D('0.00')
        for line in self.lines.all():
            discount += line.discount_excl_tax
        return discount

    @property
    def total_tax(self):
        return self.total_incl_tax - self.total_excl_tax

    @property
    def num_lines(self):
        return self.lines.count()

    @property
    def num_items(self):
        """
        Returns the number of items in this order.
        """
        num_items = 0
        for line in self.lines.all():
            num_items += line.quantity
        return num_items

    @property
    def shipping_tax(self):
        return self.shipping_incl_tax - self.shipping_excl_tax

    @property
    def shipping_status(self):
        """Return the last complete shipping event for this order."""

        # As safeguard against identical timestamps, also sort by the primary
        # key. It's not recommended to rely on this behaviour, but in practice
        # reasonably safe if PKs are not manually set.
        events = self.shipping_events.order_by('-date_created', '-pk').all()
        if not len(events):
            return ''

        # Collect all events by event-type
        event_map = OrderedDict()
        for event in events:
            event_name = event.event_type.name
            if event_name not in event_map:
                event_map[event_name] = []
            event_map[event_name].extend(list(event.line_quantities.all()))

        # Determine last complete event
        status = _("In progress")
        for event_name, event_line_quantities in event_map.items():
            if self._is_event_complete(event_line_quantities):
                return event_name
        return status

    @property
    def has_shipping_discounts(self):
        return len(self.shipping_discounts) > 0

    @property
    def shipping_before_discounts_incl_tax(self):
        # We can construct what shipping would have been before discounts by
        # adding the discounts back onto the final shipping charge.
        total = D('0.00')
        for discount in self.shipping_discounts:
            total += discount.amount
        return self.shipping_incl_tax + total

    def _is_event_complete(self, event_quantities):
        # Form map of line to quantity
        event_map = {}
        for event_quantity in event_quantities:
            line_id = event_quantity.line_id
            event_map.setdefault(line_id, 0)
            event_map[line_id] += event_quantity.quantity

        for line in self.lines.all():
            if event_map.get(line.pk, 0) != line.quantity:
                return False
        return True

    class Meta:
        abstract = True
        app_label = 'order'
        ordering = ['-date_placed']
        verbose_name = _("Order")
        verbose_name_plural = _("Orders")

    def __str__(self):
        return u"#%s" % (self.number, )

    def verification_hash(self):
        key = '%s%s' % (self.number, settings.SECRET_KEY)
        hash = hashlib.md5(key.encode('utf8'))
        return hash.hexdigest()

    @property
    def email(self):
        if not self.user:
            return self.guest_email
        return self.user.email

    @property
    def basket_discounts(self):
        # This includes both offer- and voucher- discounts.  For orders we
        # don't need to treat them differently like we do for baskets.
        return self.discounts.filter(category=AbstractOrderDiscount.BASKET)

    @property
    def shipping_discounts(self):
        return self.discounts.filter(category=AbstractOrderDiscount.SHIPPING)

    @property
    def post_order_actions(self):
        return self.discounts.filter(category=AbstractOrderDiscount.DEFERRED)

    def set_date_placed_default(self):
        if self.date_placed is None:
            self.date_placed = now()

    def save(self, *args, **kwargs):
        # Ensure the date_placed field works as it auto_now_add was set. But
        # this gives us the ability to set the date_placed explicitly (which is
        # useful when importing orders from another system).
        self.set_date_placed_default()
        super(AbstractOrder, self).save(*args, **kwargs)
Example #37
0
 def __str__(self):
     return _("Discount of %(amount)r from order %(order)s") % {
         'amount': self.amount,
         'order': self.order
     }
Example #38
0
 class Meta:
     abstract = True
     app_label = 'order'
     verbose_name = _("Shipping Event Type")
     verbose_name_plural = _("Shipping Event Types")
     ordering = ('name', )
Example #39
0
 class Meta:
     app_label = 'order'
     verbose_name = _("Shipping Event Quantity")
     verbose_name_plural = _("Shipping Event Quantities")
     unique_together = ('event', 'line')
Example #40
0
class AbstractOrderDiscount(models.Model):
    """
    A discount against an order.

    Normally only used for display purposes so an order can be listed with
    discounts displayed separately even though in reality, the discounts are
    applied at the line level.

    This has evolved to be a slightly misleading class name as this really
    track benefit applications which aren't necessarily discounts.
    """
    order = models.ForeignKey('order.Order',
                              on_delete=models.CASCADE,
                              related_name="discounts",
                              verbose_name=_("Order"))

    # We need to distinguish between basket discounts, shipping discounts and
    # 'deferred' discounts.
    BASKET, SHIPPING, DEFERRED = "Basket", "Shipping", "Deferred"
    CATEGORY_CHOICES = (
        (BASKET, _(BASKET)),
        (SHIPPING, _(SHIPPING)),
        (DEFERRED, _(DEFERRED)),
    )
    category = models.CharField(_("Discount category"),
                                default=BASKET,
                                max_length=64,
                                choices=CATEGORY_CHOICES)

    offer_id = models.PositiveIntegerField(_("Offer ID"),
                                           blank=True,
                                           null=True)
    offer_name = models.CharField(_("Offer name"),
                                  max_length=128,
                                  db_index=True,
                                  blank=True)
    voucher_id = models.PositiveIntegerField(_("Voucher ID"),
                                             blank=True,
                                             null=True)
    voucher_code = models.CharField(_("Code"),
                                    max_length=128,
                                    db_index=True,
                                    blank=True)
    frequency = models.PositiveIntegerField(_("Frequency"), null=True)
    amount = models.DecimalField(_("Amount"),
                                 decimal_places=2,
                                 max_digits=12,
                                 default=0)

    # Post-order offer applications can return a message to indicate what
    # action was taken after the order was placed.
    message = models.TextField(blank=True)

    @property
    def is_basket_discount(self):
        return self.category == self.BASKET

    @property
    def is_shipping_discount(self):
        return self.category == self.SHIPPING

    @property
    def is_post_order_action(self):
        return self.category == self.DEFERRED

    class Meta:
        abstract = True
        app_label = 'order'
        verbose_name = _("Order Discount")
        verbose_name_plural = _("Order Discounts")

    def save(self, **kwargs):
        if self.offer_id and not self.offer_name:
            offer = self.offer
            if offer:
                self.offer_name = offer.name

        if self.voucher_id and not self.voucher_code:
            voucher = self.voucher
            if voucher:
                self.voucher_code = voucher.code

        super(AbstractOrderDiscount, self).save(**kwargs)

    def __str__(self):
        return _("Discount of %(amount)r from order %(order)s") % {
            'amount': self.amount,
            'order': self.order
        }

    @property
    def offer(self):
        Offer = get_model('offer', 'ConditionalOffer')
        try:
            return Offer.objects.get(id=self.offer_id)
        except Offer.DoesNotExist:
            return None

    @property
    def voucher(self):
        Voucher = get_model('voucher', 'Voucher')
        try:
            return Voucher.objects.get(id=self.voucher_id)
        except Voucher.DoesNotExist:
            return None

    def description(self):
        if self.voucher_code:
            return self.voucher_code
        return self.offer_name or u""
Example #41
0
class User(AbstractBaseUser, PermissionsMixin):

    email = models.EmailField(
        verbose_name=_('email address'),
        max_length=255, unique=True, db_index=True,
    )
    speaker_name = models.CharField(
        verbose_name=_('speaker name'),
        max_length=100,
    )
    bio = EAWTextField(
        verbose_name=_('biography'),
        max_length=1000,
        help_text=_(
            "Describe yourself with 500 characters or less. "
            "There will be no formatting."
        ),
    )
    photo = models.ImageField(
        verbose_name=_('photo'),
        blank=True, default='', upload_to=photo_upload_to,
    )
    facebook_profile_url = models.URLField(
        verbose_name=_('Facebook'),
        blank=True,
        help_text=format_html_lazy(_(
            "Link to your Facebook profile page. This will be shown when "
            "we display your public information. If you do not know what your "
            "profile page link is, click on "
            "<a href='https://www.facebook.com/me' target='_blank'>"
            "this link</a>, and copy-paste the URL of the page opened. "
            "Remember to log in to Facebook first!"
        )),
    )
    twitter_id = models.CharField(
        verbose_name=_('Twitter'),
        blank=True, max_length=100, validators=[
            RegexValidator(r'^[0-9a-zA-Z_]*$', 'Not a valid Twitter handle'),
        ],
        help_text=_(
            "Your Twitter handle, without the \"@\" sign. This will be "
            "shown when we display your public information."
        ),
    )
    github_id = models.CharField(
        verbose_name=_('GitHub'),
        blank=True, max_length=100, validators=[
            RegexValidator(r'^[0-9a-zA-Z_-]*$', 'Not a valid GitHub account'),
        ],
        help_text=_(
            "Your GitHub account, without the \"@\" sign. This will be "
            "shown when we display your public information."
        ),
    )
    verified = models.BooleanField(
        verbose_name=_('verified'),
        default=False,
        help_text=_(
            "Designates whether the user has verified email ownership."
        ),
    )
    is_staff = models.BooleanField(
        verbose_name=_('staff status'),
        default=False,
        help_text=_(
            "Designates whether the user can log into this admin site."
        ),
    )
    is_active = models.BooleanField(
        verbose_name=_('active'),
        default=True,
        help_text=_(
            "Designates whether this user should be treated as "
            "active. Unselect this instead of deleting accounts."
        ),
    )
    date_joined = models.DateTimeField(
        verbose_name=_('date joined'),
        default=timezone.now,
    )

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        swappable = 'AUTH_USER_MODEL'

    def __str__(self):
        return self.email

    def as_hash(self):
        """Return the user's hash representation.

        Pipeline:
            - pk -> md5 -> first 2 bytes -> b16 -> str.
        """
        return '#{hash_user}'.format(
            hash_user=(
                base64.b16encode(
                    # Picking the first 2 bytes may still run into hash
                    # collisions if you take the whole users list into account,
                    # but as this is used for representing reviewers which is a
                    # comparatively small subset, it's not a big deal.
                    hashlib.md5(str(self.pk).encode('utf-8')).digest()[:2],
                ).decode('utf-8')
            )
        )

    as_hash.short_description = _('Reviewer ID')

    def get_full_name(self):
        return self.speaker_name

    def get_short_name(self):
        return self.speaker_name

    def get_photo_url(self):
        """Return the photo URL if set, otherwise a default image.
        """
        if self.photo:
            return self.photo.url
        return static('images/default_head.png')

    def is_valid_speaker(self):
        """Whether the user is a valid speaker.

        :seealso: ``UserQuerySet.get_valid_speakers``
        """
        return (
            self.verified and self.is_active and
            self.speaker_name and self.bio
        )

    def is_reviewer(self):
        return self.has_perm('reviews.add_review')

    @property
    def cospeaking_info_set(self):
        return self.additionalspeaker_set.filter(
            cancelled=False,
            conference=settings.CONFERENCE_DEFAULT_SLUG,
        )

    @property
    def twitter_profile_url(self):
        return 'https://twitter.com/{}'.format(self.twitter_id)

    @property
    def github_profile_url(self):
        return 'https://github.com/{}'.format(self.github_id)

    def get_verification_key(self):
        key = signing.dumps(
            obj=getattr(self, self.USERNAME_FIELD),
            salt=settings.SECRET_KEY,
        )
        return key

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user.
        """
        send_mail(subject, message, from_email, [self.email], **kwargs)

    def send_verification_email(self, request):
        verification_key = self.get_verification_key()
        verification_url = request.build_absolute_uri(
            reverse('user_verify', kwargs={
                'verification_key': verification_key,
            }),
        )
        context = {
            'user': self,
            'host': request.get_host(),
            'verification_key': verification_key,
            'verification_url': verification_url,
        }
        message = render_to_string(
            'registration/verification_email.txt', context,
        )
        self.email_user(
            subject=ugettext('Verify your email address on {host}').format(
                **context
            ),
            message=message, fail_silently=False,
        )
Example #42
0
 def __str__(self):
     return _("%(product)s - quantity %(qty)d") % {
         'product': self.line.product,
         'qty': self.quantity
     }
Example #43
0
 class Meta:
     db_table = 'quotes'
     ordering = ('-publish', )
     verbose_name = _('quote')
     verbose_name_plural = _('quotes')
Example #44
0
 class Meta:
     verbose_name = _('user')
     verbose_name_plural = _('users')
     swappable = 'AUTH_USER_MODEL'
Example #45
0
def import_documents(request):
    def is_reserved_directory(doc):
        return doc['fields']['type'] == 'directory' and doc['fields'][
            'name'] in (Document2.HOME_DIR, Document2.TRASH_DIR)

    try:
        if request.FILES.get('documents'):
            documents = request.FILES['documents'].read()
        else:
            documents = json.loads(request.POST.get('documents'))

        documents = json.loads(documents)
    except ValueError as e:
        raise PopupException(
            _('Failed to import documents, the file does not contain valid JSON.'
              ))

    # Validate documents
    if not _is_import_valid(documents):
        raise PopupException(
            _('Failed to import documents, the file does not contain the expected JSON schema for Hue documents.'
              ))

    docs = []

    uuids_map = dict((doc['fields']['uuid'], None) for doc in documents
                     if not is_reserved_directory(doc))

    for doc in documents:
        # Filter docs to import, ignoring reserved directories (home and Trash) and history docs
        if not is_reserved_directory(doc):
            # Remove any deprecated fields
            if 'tags' in doc['fields']:
                doc['fields'].pop('tags')

            # If doc is not owned by current user, make a copy of the document with current user as owner
            if doc['fields']['owner'][0] != request.user.username:
                doc = _copy_document_with_owner(doc, request.user, uuids_map)
            else:  # Update existing doc or create new
                doc = _create_or_update_document_with_owner(
                    doc, request.user, uuids_map)

            # For oozie docs replace dependent uuids with the newly created ones
            if doc['fields']['type'].startswith('oozie-'):
                doc = _update_imported_oozie_document(doc, uuids_map)

            # If the doc contains any history dependencies, ignore them
            # NOTE: this assumes that each dependency is exported as an array using the natural PK [uuid, version, is_history]
            deps_minus_history = [
                dep for dep in doc['fields'].get('dependencies', [])
                if len(dep) >= 3 and not dep[2]
            ]
            doc['fields']['dependencies'] = deps_minus_history

            # Replace illegal characters
            if '/' in doc['fields']['name']:
                new_name = doc['fields']['name'].replace('/', '-')
                LOG.warn(
                    "Found illegal slash in document named: %s, renaming to: %s."
                    % (doc['fields']['name'], new_name))
                doc['fields']['name'] = new_name

            # Set last modified date to now
            doc['fields']['last_modified'] = datetime.now().replace(
                microsecond=0).isoformat()
            docs.append(doc)

    f = tempfile.NamedTemporaryFile(mode='w+', suffix='.json')
    f.write(json.dumps(docs))
    f.flush()

    stdout = string_io()
    try:
        with transaction.atomic(
        ):  # We wrap both commands to commit loaddata & sync
            management.call_command('loaddata',
                                    f.name,
                                    verbosity=3,
                                    traceback=True,
                                    stdout=stdout)
            Document.objects.sync()

        if request.POST.get('redirect'):
            return redirect(request.POST.get('redirect'))
        else:
            return JsonResponse({
                'status':
                0,
                'message':
                stdout.getvalue(),
                'count':
                len(documents),
                'created_count':
                len([doc for doc in documents if doc['pk'] is None]),
                'updated_count':
                len([doc for doc in documents if doc['pk'] is not None]),
                'username':
                request.user.username,
                'documents': [
                    dict([('name', doc['fields']['name']),
                          ('uuid', doc['fields']['uuid']),
                          ('type', doc['fields']['type']),
                          ('owner', doc['fields']['owner'][0])])
                    for doc in docs
                ]
            })
    except Exception as e:
        LOG.error('Failed to run loaddata command in import_documents:\n %s' %
                  stdout.getvalue())
        return JsonResponse({'status': -1, 'message': smart_str(e)})
    finally:
        stdout.close()
Example #46
0
def formdata(request, type, parent_id, delete_id=None):
    model = None
    results = None
    response = {}

    # define types handled
    if type == "rightsnote":
        model = models.RightsStatementRightsGrantedNote
        parent_model = models.RightsStatementRightsGranted
        model_parent_field = "rightsgranted"
        model_value_fields = ["rightsgrantednote"]

        results = model.objects.filter(rightsgranted=parent_id)

    if type == "rightsrestriction":
        model = models.RightsStatementRightsGrantedRestriction
        parent_model = models.RightsStatementRightsGranted
        model_parent_field = "rightsgranted"
        model_value_fields = ["restriction"]

        results = model.objects.filter(rightsgranted=parent_id)

    if type == "licensenote":
        model = models.RightsStatementLicenseNote
        parent_model = models.RightsStatementLicense
        model_parent_field = "rightsstatementlicense"
        model_value_fields = ["licensenote"]

        results = model.objects.filter(rightsstatementlicense=parent_id)

    if type == "statutenote":
        model = models.RightsStatementStatuteInformationNote
        parent_model = models.RightsStatementStatuteInformation
        model_parent_field = "rightsstatementstatute"
        model_value_fields = ["statutenote"]

        results = model.objects.filter(rightsstatementstatute=parent_id)

    if type == "copyrightnote":
        model = models.RightsStatementCopyrightNote
        parent_model = models.RightsStatementCopyright
        model_parent_field = "rightscopyright"
        model_value_fields = ["copyrightnote"]

        results = model.objects.filter(rightscopyright=parent_id)

    if type == "copyrightdocumentationidentifier":
        model = models.RightsStatementCopyrightDocumentationIdentifier
        parent_model = models.RightsStatementCopyright
        model_parent_field = "rightscopyright"
        model_value_fields = [
            "copyrightdocumentationidentifiertype",
            "copyrightdocumentationidentifiervalue",
            "copyrightdocumentationidentifierrole",
        ]

        results = model.objects.filter(rightscopyright=parent_id)

    if type == "statutedocumentationidentifier":
        model = models.RightsStatementStatuteDocumentationIdentifier
        parent_model = models.RightsStatementStatuteInformation
        model_parent_field = "rightsstatementstatute"
        model_value_fields = [
            "statutedocumentationidentifiertype",
            "statutedocumentationidentifiervalue",
            "statutedocumentationidentifierrole",
        ]

        results = model.objects.filter(rightsstatementstatute=parent_id)

    if type == "licensedocumentationidentifier":
        model = models.RightsStatementLicenseDocumentationIdentifier
        parent_model = models.RightsStatementLicense
        model_parent_field = "rightsstatementlicense"
        model_value_fields = [
            "licensedocumentationidentifiertype",
            "licensedocumentationidentifiervalue",
            "licensedocumentationidentifierrole",
        ]

        results = model.objects.filter(rightsstatementlicense=parent_id)

    if type == "otherrightsdocumentationidentifier":
        model = models.RightsStatementOtherRightsDocumentationIdentifier
        parent_model = models.RightsStatementOtherRightsInformation
        model_parent_field = "rightsstatementotherrights"
        model_value_fields = [
            "otherrightsdocumentationidentifiertype",
            "otherrightsdocumentationidentifiervalue",
            "otherrightsdocumentationidentifierrole",
        ]

        results = model.objects.filter(rightsstatementotherrights=parent_id)

    if type == "otherrightsnote":
        model = models.RightsStatementOtherRightsInformationNote
        parent_model = models.RightsStatementOtherRightsInformation
        model_parent_field = "rightsstatementotherrights"
        model_value_fields = ["otherrightsnote"]

        results = model.objects.filter(rightsstatementotherrights=parent_id)

    # handle creation
    if request.method == "POST":
        # load or initiate model instance
        id = request.POST.get("id", 0)
        if id > 0:
            instance = model.objects.get(pk=id)
        else:
            instance = model()

        # set instance parent
        parent = parent_model.objects.filter(pk=parent_id)
        setattr(instance, model_parent_field, parent[0])

        # set instance field values using request data
        for field in model_value_fields:
            value = request.POST.get(field, "")
            setattr(instance, field, value)
        instance.save()

        if id == 0:
            response["new_id"] = instance.pk

        response["message"] = _("Added.")

    # handle deletion
    if request.method == "DELETE":
        if delete_id is None:
            response["message"] = _("Error: no delete ID supplied.")
        else:
            model.objects.filter(pk=delete_id).delete()
            response["message"] = _("Deleted.")

    # send back revised data
    if results is not None:
        response["results"] = []
        for result in results:
            values = {}
            for field in model_value_fields:
                values[field] = result.__dict__[field]
            response["results"].append({"id": result.pk, "values": values})

    if model is None:
        response["message"] = _("Incorrect type.")

    return helpers.json_response(response)
Example #47
0
    absolute_import, division, print_function, unicode_literals)

import re

from django.utils.translation import ugettext_lazy as _

THEME_DEFAULT = 'default'
THEME_SPHINX = 'sphinxdoc'
THEME_SCROLLS = 'scrolls'
THEME_AGOGO = 'agogo'
THEME_TRADITIONAL = 'traditional'
THEME_NATURE = 'nature'
THEME_HAIKU = 'haiku'

DOCUMENTATION_CHOICES = (
    ('auto', _('Automatically Choose')),
    ('sphinx', _('Sphinx Html')),
    ('mkdocs', _('Mkdocs (Markdown)')),
    ('sphinx_htmldir', _('Sphinx HtmlDir')),
    ('sphinx_singlehtml', _('Sphinx Single Page HTML')),
)

DEFAULT_THEME_CHOICES = (
    # Translators: This is a name of a Sphinx theme.
    (THEME_DEFAULT, _('Default')),
    # Translators: This is a name of a Sphinx theme.
    (THEME_SPHINX, _('Sphinx Docs')),
    # (THEME_SCROLLS, 'Scrolls'),
    # (THEME_AGOGO, 'Agogo'),
    # Translators: This is a name of a Sphinx theme.
    (THEME_TRADITIONAL, _('Traditional')),
Example #48
0
 class Meta:
     db_table = 'stories'
     ordering = ('-publish', )
     verbose_name = _('story')
     verbose_name_plural = _('stories')
Example #49
0
class AbstractConditionalOffer(models.Model):
    """
    A conditional offer (eg buy 1, get 10% off)
    """
    name = models.CharField(
        _("Name"), max_length=128, unique=True,
        help_text=_("This is displayed within the customer's basket"))
    slug = fields.AutoSlugField(
        _("Slug"), max_length=128, unique=True, populate_from='name')
    description = models.TextField(_("Description"), blank=True,
                                   help_text=_("This is displayed on the offer"
                                               " browsing page"))

    # Offers come in a few different types:
    # (a) Offers that are available to all customers on the site.  Eg a
    #     3-for-2 offer.
    # (b) Offers that are linked to a voucher, and only become available once
    #     that voucher has been applied to the basket
    # (c) Offers that are linked to a user.  Eg, all students get 10% off.  The
    #     code to apply this offer needs to be coded
    # (d) Session offers - these are temporarily available to a user after some
    #     trigger event.  Eg, users coming from some affiliate site get 10%
    #     off.
    SITE, VOUCHER, USER, SESSION = ("Site", "Voucher", "User", "Session")
    TYPE_CHOICES = (
        (SITE, _("Site offer - available to all users")),
        (VOUCHER, _("Voucher offer - only available after entering "
                    "the appropriate voucher code")),
        (USER, _("User offer - available to certain types of user")),
        (SESSION, _("Session offer - temporary offer, available for "
                    "a user for the duration of their session")),
    )
    offer_type = models.CharField(
        _("Type"), choices=TYPE_CHOICES, default=SITE, max_length=128)

    exclusive = models.BooleanField(
        _("Exclusive offer"),
        help_text=_("Exclusive offers cannot be combined on the same items"),
        default=True
    )

    # We track a status variable so it's easier to load offers that are
    # 'available' in some sense.
    OPEN, SUSPENDED, CONSUMED = "Open", "Suspended", "Consumed"
    status = models.CharField(_("Status"), max_length=64, default=OPEN)

    condition = models.ForeignKey(
        'offer.Condition',
        on_delete=models.CASCADE,
        related_name='offers',
        verbose_name=_("Condition"))
    benefit = models.ForeignKey(
        'offer.Benefit',
        on_delete=models.CASCADE,
        related_name='offers',
        verbose_name=_("Benefit"))

    # Some complicated situations require offers to be applied in a set order.
    priority = models.IntegerField(
        _("Priority"), default=0,
        help_text=_("The highest priority offers are applied first"))

    # AVAILABILITY

    # Range of availability.  Note that if this is a voucher offer, then these
    # dates are ignored and only the dates from the voucher are used to
    # determine availability.
    start_datetime = models.DateTimeField(
        _("Start date"), blank=True, null=True,
        help_text=_("Offers are active from the start date. "
                    "Leave this empty if the offer has no start date."))
    end_datetime = models.DateTimeField(
        _("End date"), blank=True, null=True,
        help_text=_("Offers are active until the end date. "
                    "Leave this empty if the offer has no expiry date."))

    # Use this field to limit the number of times this offer can be applied in
    # total.  Note that a single order can apply an offer multiple times so
    # this is not necessarily the same as the number of orders that can use it.
    # Also see max_basket_applications.
    max_global_applications = models.PositiveIntegerField(
        _("Max global applications"),
        help_text=_("The number of times this offer can be used before it "
                    "is unavailable"), blank=True, null=True)

    # Use this field to limit the number of times this offer can be used by a
    # single user.  This only works for signed-in users - it doesn't really
    # make sense for sites that allow anonymous checkout.
    max_user_applications = models.PositiveIntegerField(
        _("Max user applications"),
        help_text=_("The number of times a single user can use this offer"),
        blank=True, null=True)

    # Use this field to limit the number of times this offer can be applied to
    # a basket (and hence a single order). Often, an offer should only be
    # usable once per basket/order, so this field will commonly be set to 1.
    max_basket_applications = models.PositiveIntegerField(
        _("Max basket applications"),
        blank=True, null=True,
        help_text=_("The number of times this offer can be applied to a "
                    "basket (and order)"))

    # Use this field to limit the amount of discount an offer can lead to.
    # This can be helpful with budgeting.
    max_discount = models.DecimalField(
        _("Max discount"), decimal_places=2, max_digits=12, null=True,
        blank=True,
        help_text=_("When an offer has given more discount to orders "
                    "than this threshold, then the offer becomes "
                    "unavailable"))

    # TRACKING
    # These fields are used to enforce the limits set by the
    # max_* fields above.

    total_discount = models.DecimalField(
        _("Total Discount"), decimal_places=2, max_digits=12,
        default=D('0.00'))
    num_applications = models.PositiveIntegerField(
        _("Number of applications"), default=0)
    num_orders = models.PositiveIntegerField(
        _("Number of Orders"), default=0)

    redirect_url = fields.ExtendedURLField(
        _("URL redirect (optional)"), blank=True)
    date_created = models.DateTimeField(_("Date Created"), auto_now_add=True)

    objects = models.Manager()
    active = ActiveOfferManager()

    # We need to track the voucher that this offer came from (if it is a
    # voucher offer)
    _voucher = None

    class Meta:
        abstract = True
        app_label = 'offer'
        ordering = ['-priority', 'pk']
        verbose_name = _("Conditional offer")
        verbose_name_plural = _("Conditional offers")

    def save(self, *args, **kwargs):
        # Check to see if consumption thresholds have been broken
        if not self.is_suspended:
            if self.get_max_applications() == 0:
                self.status = self.CONSUMED
            else:
                self.status = self.OPEN

        return super().save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('offer:detail', kwargs={'slug': self.slug})

    def __str__(self):
        return self.name

    def clean(self):
        if (self.start_datetime and self.end_datetime and
                self.start_datetime > self.end_datetime):
            raise exceptions.ValidationError(
                _('End date should be later than start date'))

    @property
    def is_open(self):
        return self.status == self.OPEN

    @property
    def is_suspended(self):
        return self.status == self.SUSPENDED

    def suspend(self):
        self.status = self.SUSPENDED
        self.save()
    suspend.alters_data = True

    def unsuspend(self):
        self.status = self.OPEN
        self.save()
    unsuspend.alters_data = True

    def is_available(self, user=None, test_date=None):
        """
        Test whether this offer is available to be used
        """
        if self.is_suspended:
            return False
        if test_date is None:
            test_date = now()
        predicates = []
        if self.start_datetime:
            predicates.append(self.start_datetime > test_date)
        if self.end_datetime:
            predicates.append(test_date > self.end_datetime)
        if any(predicates):
            return False
        return self.get_max_applications(user) > 0

    def is_condition_satisfied(self, basket):
        return self.condition.proxy().is_satisfied(self, basket)

    def is_condition_partially_satisfied(self, basket):
        return self.condition.proxy().is_partially_satisfied(self, basket)

    def get_upsell_message(self, basket):
        return self.condition.proxy().get_upsell_message(self, basket)

    def apply_benefit(self, basket):
        """
        Applies the benefit to the given basket and returns the discount.
        """
        if not self.is_condition_satisfied(basket):
            return ZERO_DISCOUNT
        return self.benefit.proxy().apply(
            basket, self.condition.proxy(), self)

    def apply_deferred_benefit(self, basket, order, application):
        """
        Applies any deferred benefits.  These are things like adding loyalty
        points to someone's account.
        """
        return self.benefit.proxy().apply_deferred(basket, order, application)

    def set_voucher(self, voucher):
        self._voucher = voucher

    def get_voucher(self):
        return self._voucher

    def get_max_applications(self, user=None):
        """
        Return the number of times this offer can be applied to a basket for a
        given user.
        """
        if self.max_discount and self.total_discount >= self.max_discount:
            return 0

        # Hard-code a maximum value as we need some sensible upper limit for
        # when there are not other caps.
        limits = [10000]
        if self.max_user_applications and user:
            limits.append(max(0, self.max_user_applications -
                          self.get_num_user_applications(user)))
        if self.max_basket_applications:
            limits.append(self.max_basket_applications)
        if self.max_global_applications:
            limits.append(
                max(0, self.max_global_applications - self.num_applications))
        return min(limits)

    def get_num_user_applications(self, user):
        OrderDiscount = get_model('order', 'OrderDiscount')
        aggregates = OrderDiscount.objects.filter(offer_id=self.id,
                                                  order__user=user)\
            .aggregate(total=models.Sum('frequency'))
        return aggregates['total'] if aggregates['total'] is not None else 0

    def shipping_discount(self, charge):
        return self.benefit.proxy().shipping_discount(charge)

    def record_usage(self, discount):
        self.num_applications += discount['freq']
        self.total_discount += discount['discount']
        self.num_orders += 1
        self.save()
    record_usage.alters_data = True

    def availability_description(self):
        """
        Return a description of when this offer is available
        """
        restrictions = self.availability_restrictions()
        descriptions = [r['description'] for r in restrictions]
        return "<br/>".join(descriptions)

    def availability_restrictions(self):  # noqa (too complex (15))
        restrictions = []
        if self.is_suspended:
            restrictions.append({
                'description': _("Offer is suspended"),
                'is_satisfied': False})

        if self.max_global_applications:
            remaining = self.max_global_applications - self.num_applications
            desc = _("Limited to %(total)d uses (%(remainder)d remaining)") \
                % {'total': self.max_global_applications,
                   'remainder': remaining}
            restrictions.append({'description': desc,
                                 'is_satisfied': remaining > 0})

        if self.max_user_applications:
            if self.max_user_applications == 1:
                desc = _("Limited to 1 use per user")
            else:
                desc = _("Limited to %(total)d uses per user") \
                    % {'total': self.max_user_applications}
            restrictions.append({'description': desc,
                                 'is_satisfied': True})

        if self.max_basket_applications:
            if self.max_user_applications == 1:
                desc = _("Limited to 1 use per basket")
            else:
                desc = _("Limited to %(total)d uses per basket") \
                    % {'total': self.max_basket_applications}
            restrictions.append({
                'description': desc,
                'is_satisfied': True})

        def hide_time_if_zero(dt):
            # Only show hours/minutes if they have been specified
            if dt.tzinfo:
                localtime = dt.astimezone(get_current_timezone())
            else:
                localtime = dt
            if localtime.hour == 0 and localtime.minute == 0:
                return date_filter(localtime, settings.DATE_FORMAT)
            return date_filter(localtime, settings.DATETIME_FORMAT)

        if self.start_datetime or self.end_datetime:
            today = now()
            if self.start_datetime and self.end_datetime:
                desc = _("Available between %(start)s and %(end)s") \
                    % {'start': hide_time_if_zero(self.start_datetime),
                       'end': hide_time_if_zero(self.end_datetime)}
                is_satisfied \
                    = self.start_datetime <= today <= self.end_datetime
            elif self.start_datetime:
                desc = _("Available from %(start)s") % {
                    'start': hide_time_if_zero(self.start_datetime)}
                is_satisfied = today >= self.start_datetime
            elif self.end_datetime:
                desc = _("Available until %(end)s") % {
                    'end': hide_time_if_zero(self.end_datetime)}
                is_satisfied = today <= self.end_datetime
            restrictions.append({
                'description': desc,
                'is_satisfied': is_satisfied})

        if self.max_discount:
            desc = _("Limited to a cost of %(max)s") % {
                'max': currency(self.max_discount)}
            restrictions.append({
                'description': desc,
                'is_satisfied': self.total_discount < self.max_discount})

        return restrictions

    @property
    def has_products(self):
        return self.condition.range is not None

    def products(self):
        """
        Return a queryset of products in this offer
        """
        Product = get_model('catalogue', 'Product')
        if not self.has_products:
            return Product.objects.none()

        queryset = self.condition.range.all_products()
        return queryset.filter(is_discountable=True).exclude(
            structure=Product.CHILD)
Example #50
0
def copy_document(request):
    uuid = json.loads(request.POST.get('uuid', '""'))

    if not uuid:
        raise PopupException(_('copy_document requires uuid'))

    # Document2 and Document model objects are linked and both are saved when saving
    document = Document2.objects.get_by_uuid(user=request.user, uuid=uuid)
    # Document model object
    document1 = document.doc.get()

    if document.type == 'directory':
        raise PopupException(_('Directory copy is not supported'))

    name = document.name + '-copy'

    # Make the copy of the Document2 model object
    copy_document = document.copy(name=name, owner=request.user)
    # Make the copy of Document model object too
    document1.copy(content_object=copy_document, name=name, owner=request.user)

    # Import workspace for all oozie jobs
    if document.type == 'oozie-workflow2' or document.type == 'oozie-bundle2' or document.type == 'oozie-coordinator2':
        from oozie.models2 import Workflow, Coordinator, Bundle, _import_workspace
        # Update the name field in the json 'data' field
        if document.type == 'oozie-workflow2':
            workflow = Workflow(document=document)
            workflow.update_name(name)
            workflow.update_uuid(copy_document.uuid)
            _import_workspace(request.fs, request.user, workflow)
            copy_document.update_data(
                {'workflow': workflow.get_data()['workflow']})
            copy_document.save()

        if document.type == 'oozie-bundle2' or document.type == 'oozie-coordinator2':
            if document.type == 'oozie-bundle2':
                bundle_or_coordinator = Bundle(document=document)
            else:
                bundle_or_coordinator = Coordinator(document=document)
            json_data = bundle_or_coordinator.get_data_for_json()
            json_data['name'] = name
            json_data['uuid'] = copy_document.uuid
            copy_document.update_data(json_data)
            copy_document.save()
            _import_workspace(request.fs, request.user, bundle_or_coordinator)
    elif document.type == 'search-dashboard':
        from dashboard.models import Collection2
        collection = Collection2(request.user, document=document)
        collection.data['collection']['label'] = name
        collection.data['collection']['uuid'] = copy_document.uuid
        copy_document.update_data(
            {'collection': collection.data['collection']})
        copy_document.save()
    # Keep the document and data in sync
    else:
        copy_data = copy_document.data_dict
        if 'name' in copy_data:
            copy_data['name'] = name
        if 'uuid' in copy_data:
            copy_data['uuid'] = copy_document.uuid
        copy_document.update_data(copy_data)
        copy_document.save()

    return JsonResponse({'status': 0, 'document': copy_document.to_dict()})
Example #51
0
class AbstractRange(models.Model):
    """
    Represents a range of products that can be used within an offer.

    Ranges only support adding parent or stand-alone products. Offers will
    consider child products automatically.
    """
    name = models.CharField(_("Name"), max_length=128, unique=True)
    slug = fields.AutoSlugField(
        _("Slug"), max_length=128, unique=True, populate_from="name")

    description = models.TextField(blank=True)

    # Whether this range is public
    is_public = models.BooleanField(
        _('Is public?'), default=False,
        help_text=_("Public ranges have a customer-facing page"))

    includes_all_products = models.BooleanField(
        _('Includes all products?'), default=False)

    included_products = models.ManyToManyField(
        'catalogue.Product', related_name='includes', blank=True,
        verbose_name=_("Included Products"), through='offer.RangeProduct')
    excluded_products = models.ManyToManyField(
        'catalogue.Product', related_name='excludes', blank=True,
        verbose_name=_("Excluded Products"))
    classes = models.ManyToManyField(
        'catalogue.ProductClass', related_name='classes', blank=True,
        verbose_name=_("Product Types"))
    included_categories = models.ManyToManyField(
        'catalogue.Category', related_name='includes', blank=True,
        verbose_name=_("Included Categories"))

    # Allow a custom range instance to be specified
    proxy_class = fields.NullCharField(
        _("Custom class"), max_length=255, default=None, unique=True)

    date_created = models.DateTimeField(_("Date Created"), auto_now_add=True)

    __included_product_ids = None
    __excluded_product_ids = None
    __included_categories = None
    __class_ids = None
    __category_ids = None

    objects = models.Manager()
    browsable = BrowsableRangeManager()

    class Meta:
        abstract = True
        app_label = 'offer'
        verbose_name = _("Range")
        verbose_name_plural = _("Ranges")

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse(
            'catalogue:range', kwargs={'slug': self.slug})

    @cached_property
    def proxy(self):
        if self.proxy_class:
            return load_proxy(self.proxy_class)()

    def add_product(self, product, display_order=None):
        """ Add product to the range

        When adding product that is already in the range, prevent re-adding it.
        If display_order is specified, update it.

        Default display_order for a new product in the range is 0; this puts
        the product at the top of the list.
        """

        initial_order = display_order or 0
        RangeProduct = get_model('offer', 'RangeProduct')
        relation, __ = RangeProduct.objects.get_or_create(
            range=self, product=product,
            defaults={'display_order': initial_order})

        if (display_order is not None and
                relation.display_order != display_order):
            relation.display_order = display_order
            relation.save()

        # Remove product from excluded products if it was removed earlier and
        # re-added again, thus it returns back to the range product list.
        if product.id in self._excluded_product_ids():
            self.excluded_products.remove(product)
            self.invalidate_cached_ids()

    def remove_product(self, product):
        """
        Remove product from range. To save on queries, this function does not
        check if the product is in fact in the range.
        """
        RangeProduct = get_model('offer', 'RangeProduct')
        RangeProduct.objects.filter(range=self, product=product).delete()
        # Making sure product will be excluded from range products list by adding to
        # respective field. Otherwise, it could be included as a product from included
        # category or etc.
        self.excluded_products.add(product)
        # Invalidating cached property value with list of IDs of already excluded products.
        self.invalidate_cached_ids()

    def contains_product(self, product):  # noqa (too complex (12))
        """
        Check whether the passed product is part of this range.
        """

        # Delegate to a proxy class if one is provided
        if self.proxy:
            return self.proxy.contains_product(product)

        excluded_product_ids = self._excluded_product_ids()
        if product.id in excluded_product_ids:
            return False
        if self.includes_all_products:
            return True
        class_ids = self._class_ids()
        if class_ids and product.get_product_class().id in class_ids:
            return True
        included_product_ids = self._included_product_ids()
        # If the product's parent is in the range, the child is automatically included as well
        if product.is_child and product.parent.id in included_product_ids:
            return True
        if product.id in included_product_ids:
            return True
        test_categories = self._included_categories()
        if test_categories:
            for category in product.get_categories().only(
                    *self._category_comparison_fields):
                for test_category in test_categories:
                    if category == test_category \
                            or category.is_descendant_of(test_category):
                        return True
        return False

    # Deprecated alias
    @deprecated
    def contains(self, product):
        return self.contains_product(product)

    def __get_pks_and_child_pks(self, queryset):
        """
        Expects a product queryset; gets the primary keys of the passed
        products and their children.

        Verbose, but database and memory friendly.
        """
        # One query to get parent and children; [(4, None), (5, 10), (5, 11)]
        pk_tuples_iterable = queryset.values_list('pk', 'children__pk')
        # Flatten list without unpacking; [4, None, 5, 10, 5, 11]
        flat_iterable = itertools.chain.from_iterable(pk_tuples_iterable)
        # Ensure uniqueness and remove None; {4, 5, 10, 11}
        return set(flat_iterable) - {None}

    @cached_property
    def _category_comparison_fields(self):
        # Overwritten Category models could contain a lot of data, e.g CMS
        # content. Hence, this avoids fetching unneeded data in the costly
        # range comparison queries. Note that using .only() with an empty list
        # is a no-op essentially, so nothing breaks when the field is missing.
        Category = get_model('catalogue', 'Category')
        return getattr(Category, 'COMPARISON_FIELDS', ())

    def _included_categories(self):
        if not self.id:
            return self.included_categories.none()
        if self.__included_categories is None:
            self.__included_categories = self.included_categories.only(
                *self._category_comparison_fields)
        return self.__included_categories

    def _included_product_ids(self):
        if not self.id:
            return []
        if self.__included_product_ids is None:
            self.__included_product_ids = self.__get_pks_and_child_pks(
                self.included_products)
        return self.__included_product_ids

    def _excluded_product_ids(self):
        if not self.id:
            return []
        if self.__excluded_product_ids is None:
            self.__excluded_product_ids = self.__get_pks_and_child_pks(
                self.excluded_products)
        return self.__excluded_product_ids

    def _class_ids(self):
        if self.__class_ids is None:
            self.__class_ids = self.classes.values_list('pk', flat=True)
        return self.__class_ids

    def _category_ids(self):
        if self.__category_ids is None:
            ids = []
            for category in self._included_categories():
                children_ids = category.get_descendants().values_list(
                    'pk', flat=True)
                ids.append(category.pk)
                ids.extend(list(children_ids))

            self.__category_ids = ids

        return self.__category_ids

    def invalidate_cached_ids(self):
        self.__category_ids = None
        self.__included_categories = None
        self.__included_product_ids = None
        self.__excluded_product_ids = None

    def num_products(self):
        # Delegate to a proxy class if one is provided
        if self.proxy:
            return self.proxy.num_products()
        if self.includes_all_products:
            return None
        return self.all_products().count()

    def all_products(self):
        """
        Return a queryset containing all the products in the range

        This includes included_products plus the products contained in the
        included classes and categories, minus the products in
        excluded_products.
        """
        if self.proxy:
            return self.proxy.all_products()

        Product = get_model("catalogue", "Product")
        if self.includes_all_products:
            # Filter out child products and blacklisted products
            return Product.objects.browsable().exclude(
                id__in=self._excluded_product_ids())

        return Product.objects.filter(
            Q(id__in=self._included_product_ids()) |
            Q(product_class_id__in=self._class_ids()) |
            Q(productcategory__category_id__in=self._category_ids())
        ).exclude(id__in=self._excluded_product_ids()).distinct()

    @property
    def is_editable(self):
        """
        Test whether this range can be edited in the dashboard.
        """
        return not self.proxy_class

    @property
    def is_reorderable(self):
        """
        Test whether products for the range can be re-ordered.
        """
        return len(self._class_ids()) == 0 and len(self._included_categories()) == 0
Example #52
0
class EMail(Auditable, models.Model):
    to = models.CharField(
        max_length=1000,
        verbose_name=_('To'),
        help_text=_('One email address or several addresses separated by commas.'),
    )
    reply_to = models.CharField(
        max_length=1000,
        null=True, blank=True,
        verbose_name=_('Reply-To'),
    )
    cc = models.CharField(
        max_length=1000,
        null=True, blank=True,
        verbose_name=_('CC'),
        help_text=_('One email address or several addresses separated by commas.'),
    )
    bcc = models.CharField(
        max_length=1000,
        null=True, blank=True,
        verbose_name=_('BCC'),
        help_text=_('One email address or several addresses separated by commas.'),
    )
    subject = models.CharField(
        max_length=200,
        verbose_name=_('Subject'),
    )
    members = models.ManyToManyField(
        to='members.Member',
        related_name='emails',
    )
    text = models.TextField(verbose_name=_('Text'))
    sent = models.DateTimeField(null=True, blank=True, verbose_name=_('Sent at'))
    template = models.ForeignKey(to=MailTemplate, null=True, blank=True, on_delete=models.SET_NULL)
    attachments = models.ManyToManyField(
        to='documents.Document',
        related_name='mails',
    )

    @property
    def attachment_ids(self):
        if hasattr(self, 'attachments'):
            return list(self.attachments.all().values_list('pk', flat=True))
        return []

    def send(self):
        if self.sent:
            raise Exception('This mail has been sent already. It cannot be sent again.')

        from byro.mails.send import mail_send_task
        mail_send_task(
            to=self.to.split(','),
            subject=self.subject,
            body=self.text,
            sender=self.reply_to,
            cc=(self.cc or '').split(','),
            bcc=(self.bcc or '').split(','),
            attachments=self.attachment_ids,
        )

        self.sent = now()
        self.save(update_fields=['sent'])

    def copy_to_draft(self):
        new_mail = deepcopy(self)
        new_mail.pk = None
        new_mail.sent = None
        new_mail.save()
        return new_mail
Example #53
0
class AbstractCondition(BaseOfferMixin, models.Model):
    """
    A condition for an offer to be applied. You can either specify a custom
    proxy class, or need to specify a type, range and value.
    """
    COUNT, VALUE, COVERAGE = ("Count", "Value", "Coverage")
    TYPE_CHOICES = (
        (COUNT, _("Depends on number of items in basket that are in "
                  "condition range")),
        (VALUE, _("Depends on value of items in basket that are in "
                  "condition range")),
        (COVERAGE, _("Needs to contain a set number of DISTINCT items "
                     "from the condition range")))
    range = models.ForeignKey(
        'offer.Range',
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        verbose_name=_("Range"))
    type = models.CharField(_('Type'), max_length=128, choices=TYPE_CHOICES,
                            blank=True)
    value = fields.PositiveDecimalField(
        _('Value'), decimal_places=2, max_digits=12, null=True, blank=True)

    proxy_class = fields.NullCharField(
        _("Custom class"), max_length=255, default=None)

    class Meta:
        abstract = True
        app_label = 'offer'
        verbose_name = _("Condition")
        verbose_name_plural = _("Conditions")

    @property
    def proxy_map(self):
        return {
            self.COUNT: get_class(
                'offer.conditions', 'CountCondition'),
            self.VALUE: get_class(
                'offer.conditions', 'ValueCondition'),
            self.COVERAGE: get_class(
                'offer.conditions', 'CoverageCondition'),
        }

    def consume_items(self, offer, basket, affected_lines):
        pass

    def is_satisfied(self, offer, basket):
        """
        Determines whether a given basket meets this condition.  This is
        stubbed in this top-class object.  The subclassing proxies are
        responsible for implementing it correctly.
        """
        return False

    def is_partially_satisfied(self, offer, basket):
        """
        Determine if the basket partially meets the condition.  This is useful
        for up-selling messages to entice customers to buy something more in
        order to qualify for an offer.
        """
        return False

    def get_upsell_message(self, offer, basket):
        return None

    def can_apply_condition(self, line):
        """
        Determines whether the condition can be applied to a given basket line
        """
        if not line.stockrecord_id:
            return False
        product = line.product
        return (self.range.contains_product(product)
                and product.get_is_discountable())

    def get_applicable_lines(self, offer, basket, most_expensive_first=True):
        """
        Return line data for the lines that can be consumed by this condition
        """
        line_tuples = []
        for line in basket.all_lines():
            if not self.can_apply_condition(line):
                continue

            price = unit_price(offer, line)
            if not price:
                continue
            line_tuples.append((price, line))
        key = operator.itemgetter(0)
        if most_expensive_first:
            return sorted(line_tuples, reverse=True, key=key)
        return sorted(line_tuples, key=key)
Example #54
0
 class Meta:
     abstract = True
     app_label = 'offer'
     verbose_name = _("Range")
     verbose_name_plural = _("Ranges")
Example #55
0
 class Meta:
     abstract = True
     app_label = 'offer'
     verbose_name = _("Benefit")
     verbose_name_plural = _("Benefits")
Example #56
0
 class Meta:
     abstract = True
     app_label = 'offer'
     verbose_name = _("Condition")
     verbose_name_plural = _("Conditions")
Example #57
0
    def availability_restrictions(self):  # noqa (too complex (15))
        restrictions = []
        if self.is_suspended:
            restrictions.append({
                'description': _("Offer is suspended"),
                'is_satisfied': False})

        if self.max_global_applications:
            remaining = self.max_global_applications - self.num_applications
            desc = _("Limited to %(total)d uses (%(remainder)d remaining)") \
                % {'total': self.max_global_applications,
                   'remainder': remaining}
            restrictions.append({'description': desc,
                                 'is_satisfied': remaining > 0})

        if self.max_user_applications:
            if self.max_user_applications == 1:
                desc = _("Limited to 1 use per user")
            else:
                desc = _("Limited to %(total)d uses per user") \
                    % {'total': self.max_user_applications}
            restrictions.append({'description': desc,
                                 'is_satisfied': True})

        if self.max_basket_applications:
            if self.max_user_applications == 1:
                desc = _("Limited to 1 use per basket")
            else:
                desc = _("Limited to %(total)d uses per basket") \
                    % {'total': self.max_basket_applications}
            restrictions.append({
                'description': desc,
                'is_satisfied': True})

        def hide_time_if_zero(dt):
            # Only show hours/minutes if they have been specified
            if dt.tzinfo:
                localtime = dt.astimezone(get_current_timezone())
            else:
                localtime = dt
            if localtime.hour == 0 and localtime.minute == 0:
                return date_filter(localtime, settings.DATE_FORMAT)
            return date_filter(localtime, settings.DATETIME_FORMAT)

        if self.start_datetime or self.end_datetime:
            today = now()
            if self.start_datetime and self.end_datetime:
                desc = _("Available between %(start)s and %(end)s") \
                    % {'start': hide_time_if_zero(self.start_datetime),
                       'end': hide_time_if_zero(self.end_datetime)}
                is_satisfied \
                    = self.start_datetime <= today <= self.end_datetime
            elif self.start_datetime:
                desc = _("Available from %(start)s") % {
                    'start': hide_time_if_zero(self.start_datetime)}
                is_satisfied = today >= self.start_datetime
            elif self.end_datetime:
                desc = _("Available until %(end)s") % {
                    'end': hide_time_if_zero(self.end_datetime)}
                is_satisfied = today <= self.end_datetime
            restrictions.append({
                'description': desc,
                'is_satisfied': is_satisfied})

        if self.max_discount:
            desc = _("Limited to a cost of %(max)s") % {
                'max': currency(self.max_discount)}
            restrictions.append({
                'description': desc,
                'is_satisfied': self.total_discount < self.max_discount})

        return restrictions
Example #58
0
 def clean_fixed_price(self):
     if self.range:
         raise exceptions.ValidationError(
             _("No range should be selected as the condition range will "
               "be used instead."))
Example #59
0
 def clean(self):
     if (self.start_datetime and self.end_datetime and
             self.start_datetime > self.end_datetime):
         raise exceptions.ValidationError(
             _('End date should be later than start date'))
Example #60
0
class AbstractBenefit(BaseOfferMixin, models.Model):
    range = models.ForeignKey(
        'offer.Range',
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        verbose_name=_("Range"))

    # Benefit types
    PERCENTAGE, FIXED, MULTIBUY, FIXED_PRICE = (
        "Percentage", "Absolute", "Multibuy", "Fixed price")
    SHIPPING_PERCENTAGE, SHIPPING_ABSOLUTE, SHIPPING_FIXED_PRICE = (
        'Shipping percentage', 'Shipping absolute', 'Shipping fixed price')
    TYPE_CHOICES = (
        (PERCENTAGE, _("Discount is a percentage off of the product's value")),
        (FIXED, _("Discount is a fixed amount off of the product's value")),
        (MULTIBUY, _("Discount is to give the cheapest product for free")),
        (FIXED_PRICE,
         _("Get the products that meet the condition for a fixed price")),
        (SHIPPING_ABSOLUTE,
         _("Discount is a fixed amount of the shipping cost")),
        (SHIPPING_FIXED_PRICE, _("Get shipping for a fixed price")),
        (SHIPPING_PERCENTAGE, _("Discount is a percentage off of the shipping"
                                " cost")),
    )
    type = models.CharField(
        _("Type"), max_length=128, choices=TYPE_CHOICES, blank=True)

    # The value to use with the designated type.  This can be either an integer
    # (eg for multibuy) or a decimal (eg an amount) which is slightly
    # confusing.
    value = fields.PositiveDecimalField(
        _("Value"), decimal_places=2, max_digits=12, null=True, blank=True)

    # If this is not set, then there is no upper limit on how many products
    # can be discounted by this benefit.
    max_affected_items = models.PositiveIntegerField(
        _("Max Affected Items"), blank=True, null=True,
        help_text=_("Set this to prevent the discount consuming all items "
                    "within the range that are in the basket."))

    # A custom benefit class can be used instead.  This means the
    # type/value/max_affected_items fields should all be None.
    proxy_class = fields.NullCharField(
        _("Custom class"), max_length=255, default=None)

    class Meta:
        abstract = True
        app_label = 'offer'
        verbose_name = _("Benefit")
        verbose_name_plural = _("Benefits")

    @property
    def proxy_map(self):
        return {
            self.PERCENTAGE: get_class(
                'offer.benefits', 'PercentageDiscountBenefit'),
            self.FIXED: get_class(
                'offer.benefits', 'AbsoluteDiscountBenefit'),
            self.MULTIBUY: get_class(
                'offer.benefits', 'MultibuyDiscountBenefit'),
            self.FIXED_PRICE: get_class(
                'offer.benefits', 'FixedPriceBenefit'),
            self.SHIPPING_ABSOLUTE: get_class(
                'offer.benefits', 'ShippingAbsoluteDiscountBenefit'),
            self.SHIPPING_FIXED_PRICE: get_class(
                'offer.benefits', 'ShippingFixedPriceBenefit'),
            self.SHIPPING_PERCENTAGE: get_class(
                'offer.benefits', 'ShippingPercentageDiscountBenefit')
        }

    def apply(self, basket, condition, offer):
        return ZERO_DISCOUNT

    def apply_deferred(self, basket, order, application):
        return None

    def clean(self):
        if not self.type:
            return
        method_name = 'clean_%s' % self.type.lower().replace(' ', '_')
        if hasattr(self, method_name):
            getattr(self, method_name)()

    def clean_multibuy(self):
        errors = []

        if not self.range:
            errors.append(_("Multibuy benefits require a product range"))
        if self.value:
            errors.append(_("Multibuy benefits don't require a value"))
        if self.max_affected_items:
            errors.append(_("Multibuy benefits don't require a "
                            "'max affected items' attribute"))

        if errors:
            raise exceptions.ValidationError(errors)

    def clean_percentage(self):
        errors = []

        if not self.range:
            errors.append(_("Percentage benefits require a product range"))

        if not self.value:
            errors.append(_("Percentage discount benefits require a value"))
        elif self.value > 100:
            errors.append(_("Percentage discount cannot be greater than 100"))

        if errors:
            raise exceptions.ValidationError(errors)

    def clean_shipping_absolute(self):
        errors = []
        if not self.value:
            errors.append(_("A discount value is required"))
        if self.range:
            errors.append(_("No range should be selected as this benefit does "
                            "not apply to products"))
        if self.max_affected_items:
            errors.append(_("Shipping discounts don't require a "
                            "'max affected items' attribute"))

        if errors:
            raise exceptions.ValidationError(errors)

    def clean_shipping_percentage(self):
        errors = []

        if not self.value:
            errors.append(_("Percentage discount benefits require a value"))
        elif self.value > 100:
            errors.append(_("Percentage discount cannot be greater than 100"))

        if self.range:
            errors.append(_("No range should be selected as this benefit does "
                            "not apply to products"))
        if self.max_affected_items:
            errors.append(_("Shipping discounts don't require a "
                            "'max affected items' attribute"))
        if errors:
            raise exceptions.ValidationError(errors)

    def clean_shipping_fixed_price(self):
        errors = []
        if self.range:
            errors.append(_("No range should be selected as this benefit does "
                            "not apply to products"))
        if self.max_affected_items:
            errors.append(_("Shipping discounts don't require a "
                            "'max affected items' attribute"))

        if errors:
            raise exceptions.ValidationError(errors)

    def clean_fixed_price(self):
        if self.range:
            raise exceptions.ValidationError(
                _("No range should be selected as the condition range will "
                  "be used instead."))

    def clean_absolute(self):
        errors = []
        if not self.range:
            errors.append(_("Fixed discount benefits require a product range"))
        if not self.value:
            errors.append(_("Fixed discount benefits require a value"))

        if errors:
            raise exceptions.ValidationError(errors)

    def round(self, amount):
        """
        Apply rounding to discount amount
        """
        if hasattr(settings, 'OSCAR_OFFER_ROUNDING_FUNCTION'):
            return settings.OSCAR_OFFER_ROUNDING_FUNCTION(amount)
        return amount.quantize(D('.01'), ROUND_DOWN)

    def _effective_max_affected_items(self):
        """
        Return the maximum number of items that can have a discount applied
        during the application of this benefit
        """
        return self.max_affected_items if self.max_affected_items else 10000

    def can_apply_benefit(self, line):
        """
        Determines whether the benefit can be applied to a given basket line
        """
        return line.stockrecord and line.product.is_discountable

    def get_applicable_lines(self, offer, basket, range=None):
        """
        Return the basket lines that are available to be discounted

        :basket: The basket
        :range: The range of products to use for filtering.  The fixed-price
                benefit ignores its range and uses the condition range
        """
        if range is None:
            range = self.range
        line_tuples = []
        for line in basket.all_lines():
            product = line.product

            if (not range.contains_product(product) or not self.can_apply_benefit(line)):
                continue

            price = unit_price(offer, line)
            if not price:
                # Avoid zero price products
                continue
            if line.quantity_without_offer_discount(offer) == 0:
                continue
            line_tuples.append((price, line))

        # We sort lines to be cheapest first to ensure consistent applications
        return sorted(line_tuples, key=operator.itemgetter(0))

    def shipping_discount(self, charge):
        return D('0.00')