def test_get_prep_value_none(self):
     """
     Note: django migrations will call get_prep_value() with None
     see: https://github.com/SmileyChris/django-countries/issues/215
     """
     country_field_instance = CountryField(multiple=True, blank=True)
     prep_value = country_field_instance.get_prep_value(None)
     self.assertEqual(prep_value, "")
 def test_get_prep_value_empty_string(self):
     country_field_instance = CountryField(multiple=True, blank=True)
     prep_value = country_field_instance.get_prep_value("")
     self.assertEqual(prep_value, "")
Example #3
0
class Order(models.Model):
    order_number = models.CharField(max_length=32, null=False, editable=False)
    user_profile = models.ForeignKey(UserProfile,
                                     on_delete=models.SET_NULL,
                                     null=True,
                                     blank=True,
                                     related_name='orders')
    project_name = models.CharField(max_length=50, null=False, blank=False)
    project_description = models.TextField(max_length=500,
                                           null=False,
                                           blank=False)
    project_owner_full_name = models.CharField(max_length=50,
                                               null=False,
                                               blank=False)
    project_owner_email = models.EmailField(max_length=254,
                                            null=False,
                                            blank=False)
    project_owner_phone_number = models.CharField(max_length=20,
                                                  null=False,
                                                  blank=False)
    full_name = models.CharField(max_length=50, null=False, blank=False)
    email = models.EmailField(max_length=254, null=False, blank=False)
    phone_number = models.CharField(max_length=20, null=False, blank=False)
    country = CountryField(blank_label='Country *', null=False, blank=False)
    postcode = models.CharField(max_length=20, null=True, blank=True)
    town_or_city = models.CharField(max_length=40, null=False, blank=False)
    street_address1 = models.CharField(max_length=80, null=False, blank=False)
    street_address2 = models.CharField(max_length=80, null=True, blank=True)
    county = models.CharField(max_length=80, null=True, blank=True)
    date = models.DateTimeField(auto_now_add=True)
    vat = models.DecimalField(max_digits=6,
                              decimal_places=2,
                              null=False,
                              default=0)
    order_total = models.DecimalField(max_digits=10,
                                      decimal_places=2,
                                      null=False,
                                      default=0)
    grand_total = models.DecimalField(max_digits=10,
                                      decimal_places=2,
                                      null=False,
                                      default=0)
    original_bag = models.TextField(null=False, blank=False, default='')
    stripe_pid = models.CharField(max_length=254,
                                  null=False,
                                  blank=False,
                                  default='')

    def _generate_order_number(self):
        """
        Generate a random, unique order number using UUID
        """
        return uuid.uuid4().hex.upper()

    def update_total(self):
        """
        Update grand total each time a line item is added,
        accounting for delivery costs.
        """
        self.order_total = self.lineitems.aggregate(
            Sum('lineitem_total'))['lineitem_total__sum'] or 0
        self.vat = 0
        self.vat = self.order_total * Decimal(
            settings.STANDARD_QUOTE_PERCENTAGE * 0.02)
        if self.order_total < settings.FREE_QUOTE_THRESHOLD:
            self.quote_cost = Decimal(
                self.order_total * settings.STANDARD_QUOTE_PERCENTAGE /
                100) + self.vat
        else:
            self.quote_cost = 0
        self.grand_total = self.order_total + self.quote_cost + self.vat
        self.save()

    def save(self, *args, **kwargs):
        """
        Override the original save method to set the order number
        if it hasn't been set already.
        """
        if not self.order_number:
            self.order_number = self._generate_order_number()
        super().save(*args, **kwargs)

    def __str__(self):
        return self.order_number
Example #4
0
class ExternalEvent(ExternalContent):
    resource_type = "event"

    start_date = DateField(
        default=datetime.date.today,
        help_text="The date the event is scheduled to start",
    )
    end_date = DateField(
        blank=True, null=True, help_text="The date the event is scheduled to end"
    )
    city = CharField(max_length=100, blank=True, default="")
    country = CountryField(verbose_name="Country or Region", blank=True, default="")

    meta_panels = [
        MultiFieldPanel(
            [FieldPanel("start_date"), FieldPanel("end_date")], heading="Event details"
        ),
        MultiFieldPanel(
            [FieldPanel("city"), FieldPanel("country")], heading="Event address"
        ),
        InlinePanel(
            "topics",
            heading="Topics",
            help_text=(
                "Optional topics this event is associated with. "
                "Adds the event to the list of events on those topic pages"
            ),
        ),
        InlinePanel(
            "speakers",
            heading="Speakers",
            help_text=(
                "Optional speakers associated with this event. "
                "Adds the event to the list of events on their profile pages"
            ),
        ),
    ]

    settings_panels = BasePage.settings_panels + [FieldPanel("slug")]

    edit_handler = TabbedInterface(
        [
            ObjectList(ExternalContent.card_panels, heading="Card"),
            ObjectList(meta_panels, heading="Meta"),
            ObjectList(settings_panels, heading="Settings", classname="settings"),
        ]
    )

    @property
    def event(self):
        return self

    @property
    def month_group(self):
        return self.start_date.replace(day=1)

    @property
    def country_group(self):
        return (
            {"slug": self.country.code.lower(), "title": self.country.name}
            if self.country
            else {"slug": ""}
        )

    @property
    def event_dates(self):
        """Return a formatted string of the event start and end dates"""
        event_dates = self.start_date.strftime("%b %-d")
        if self.end_date:
            event_dates += " &ndash; "
            start_month = self.start_date.strftime("%m")
            if self.end_date.strftime("%m") == start_month:
                event_dates += self.end_date.strftime("%-d")
            else:
                event_dates += self.end_date.strftime("%b %-d")
        return event_dates

    @property
    def event_dates_full(self):
        """Return a formatted string of the event start and end dates,
        including the year"""
        return self.event_dates + self.start_date.strftime(", %Y")
Example #5
0
class CheckoutForm(forms.Form):
    first_name = forms.CharField(required=True)
    last_name = forms.CharField(required=True)
    email = forms.EmailField(required=True)
    contact1 = forms.CharField(required=True)
    contact2 = forms.CharField(required=False)

    shipping_address1 = forms.CharField(
        required=False,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': '1234 Main Street'
        }))
    shipping_address2 = forms.CharField(
        required=False,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': 'Apartment or suite'
        }))
    shipping_country = CountryField(blank_label='(select country)').formfield(
        required=False,
        widget=CountrySelectWidget(
            attrs={'class': 'custom-select d-block w-100'},
            layout=
            '{widget}<img class="country-select-flag" id="{flag_id}" style="margin: 6px 4px 0" src="{country.flag}">'
        ))
    shipping_zip_code = forms.CharField(
        required=False,
        widget=forms.TextInput(attrs={'class': 'form-control'}))

    billing_address1 = forms.CharField(
        required=False,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': '1234 Main Street'
        }))
    billing_address2 = forms.CharField(
        required=False,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': 'Apartment or suite'
        }))
    billing_country = CountryField(blank_label='(select country)').formfield(
        required=False,
        widget=CountrySelectWidget(
            attrs={'class': 'custom-select d-block w-100'},
            layout=
            '{widget}<img class="country-select-flag" id="{flag_id}" style="margin: 6px 4px 0" src="{country.flag}">'
        ))
    billing_zip_code = forms.CharField(
        required=False,
        widget=forms.TextInput(attrs={'class': 'form-control'}))

    chk_same_address = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={'class': 'custom-control-input'}))
    chk_save_shipping_info = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={'class': 'custom-control-input'}))
    chk_save_billing_info = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={'class': 'custom-control-input'}))
    chk_use_default_shipping = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={'class': 'custom-control-input'}))
    chk_use_default_billing = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={'class': 'custom-control-input'}))

    payment_option = forms.ChoiceField(
        widget=forms.RadioSelect(attrs={'class': 'custom-control-input'}),
        choices=PAYMENT_CHOICES)
Example #6
0
class Room(core_models.TimeStampdModel):
    """ Room Model """

    name = models.CharField(max_length=140)
    description = models.TextField()
    country = CountryField()
    city = models.CharField(max_length=80)
    price = models.IntegerField()
    address = models.CharField(max_length=140)
    guests = models.IntegerField(
        help_text="How many people will be staying ? ")
    beds = models.IntegerField()
    bedrooms = models.IntegerField()
    baths = models.IntegerField()
    check_in = models.TimeField()
    check_out = models.TimeField()
    instant_book = models.BooleanField(default=False)
    host = models.ForeignKey("users.User",
                             related_name="rooms",
                             on_delete=models.CASCADE)
    room_type = models.ForeignKey("RoomType",
                                  related_name="rooms",
                                  on_delete=models.SET_NULL,
                                  null=True)
    amenities = models.ManyToManyField("Amenity",
                                       related_name="rooms",
                                       blank=True)
    facilitys = models.ManyToManyField("Facility",
                                       related_name="rooms",
                                       blank=True)
    houserules = models.ManyToManyField("HouseRule",
                                        related_name="rooms",
                                        blank=True)

    def __str__(self):
        return self.name

    # https://docs.djangoproject.com/en/3.0/ref/models/instances/
    def save(self, *args, **kwargs):
        self.city = str.capitalize(self.city)
        super().save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse("rooms:detail", kwargs={"pk": self.pk})

    def total_rating(self):
        all_reviews = self.reviews.all()
        all_ratings = 0
        if len(all_reviews) > 0:
            for review in all_reviews:
                all_ratings += review.rating_average()
            return round(all_ratings / len(all_reviews))
        return 0

    def first_photo(self):
        try:
            (photo, ) = self.photos.all()[:1]
            # ↑ array 에서 unpacking
            return photo.file.url
        except ValueError:
            None

    def get_next_four_photo(self):
        photos = self.photos.all()[1:5]
        return photos

    def get_calendars(self):
        now = timezone.now()
        this_year = now.year
        this_month = now.month
        next_month = this_month + 1
        if this_month == 12:
            next_month = 1
        this_month_cal = Calendar(this_year, this_month)
        next_month_cal = Calendar(this_year, next_month)
        return (this_month_cal, next_month_cal)
Example #7
0
class OrgProfile(models.Model):

    user = models.ForeignKey(
        User, on_delete=models.CASCADE, null=True, blank=True, verbose_name=_("اسم المستخدم"), help_text=_('المستخدمون الذين ليس لديهم طلبات بروفايل فقط'))

    staff = models.CharField(max_length=100, null=True, blank=True)

    name = models.CharField(max_length=255, null=False,
                            verbose_name=_("اسم المنظمة"))
    name_en_ku = models.CharField(max_length=255, null=True, blank=True,
                                  verbose_name=_("اسم المنظمة باللغة الانكليزية أو الكردية"))
    short_cut = models.CharField(
        max_length=255, null=True, blank=True, verbose_name=_("الاسم المختصر"))
    org_type = models.CharField(
        max_length=150, null=False, choices=MyChoices.type_CHOICES, verbose_name=_("نوع المنظمة"))

    # position_work = CountryField(
    #     max_length=255, null=False, verbose_name=_("مكان العمل"))

    # city_work = models.ForeignKey(
    #     City, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("المحافظة"))
    # city_work = models.CharField(
    #     max_length=150, choices=syr_city_CHOICES, null=True, blank=True, verbose_name=_("المحافظة"))

    # position = models.ForeignKey(
    #     Position, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('مكان العمل'))

    logo = models.ImageField(upload_to="org_logos",
                             null=True, blank=True, default='org_logos/default_logo.jpg', verbose_name=_("شعار المنظمة"))
    message = RichTextField(
        max_length=5000, null=False, verbose_name=_("الرؤية و الرسالة"))
    message_en = RichTextField(
        max_length=5000, null=True, blank=True, verbose_name=_("الرؤية و الرسالة باللغة الانكليزية"))

    name_managing_director = models.CharField(
        max_length=255, null=True, blank=True, verbose_name=_("اسم رئيس مجلس اﻹدارة"))
    name_ceo = models.CharField(
        max_length=255, null=True, blank=True, verbose_name=_('اسم المدير التنفيذي'))
    name_ceo_en = models.CharField(
        max_length=255, null=True, blank=True, verbose_name=_('اسم المدير التنفيذي باللغة الانجليزية'))

    # CONTACT INFO
    site_web = models.URLField(
        max_length=255, null=True, blank=True, verbose_name=_('الموقع الالكتروني'))
    facebook = models.URLField(
        max_length=255, null=True, blank=True, verbose_name=_('صفحة فيسبوك'))
    twitter = models.URLField(
        max_length=255, null=True, blank=True, verbose_name=_('صفحة تويتر'))
    email = models.EmailField(max_length=255, null=True,
                              blank=True, verbose_name=_('البريد الاكتروني'))
    phone = models.CharField(max_length=100, null=True,
                             blank=True, verbose_name=_('رقم الهاتف'))
    name_person_contact = models.CharField(
        max_length=255, null=True, blank=True, verbose_name=_('اسم الشخص المسؤول عن التواصل'))
    email_person_contact = models.EmailField(
        max_length=255, null=True, blank=True, verbose_name=_('البريد الاكتروني للشخص المسؤول عن التواصل'))
    org_adress = models.CharField(
        max_length=255, null=False, verbose_name=_('عنوان المقر الرئيسي'))

    # ORG INFO
    work_domain = MultiSelectField(
        max_length=255, choices=MyChoices.domain_CHOICES, null=False, verbose_name=_('مجال العمل'))
    target_cat = MultiSelectField(
        max_length=255, null=False, choices=MyChoices.target_CHOICES, verbose_name=_('الفئات المستهدفة'))
    date_of_establishment = models.CharField(
        max_length=150, null=True, blank=True, verbose_name=_('تاريخ سنة التأسيس'))
    is_org_registered = models.CharField(
        max_length=100, null=False, choices=MyChoices.bool_CHOICES, verbose_name=_('هل المنظمة مسجلة ؟'))
    org_registered_country = CountryField(
        max_length=255, null=True, blank=True, verbose_name=_("بلد التسجيل"))

    org_members_count = models.PositiveIntegerField(
        validators=[MinValueValidator(1)], null=True, blank=True, verbose_name=_('عدد اﻷعضاء'))
    org_members_womans_count = models.PositiveIntegerField(
        validators=[MinValueValidator(1)], null=True, blank=True, verbose_name=_('عدد النساء من اﻷعضاء'))
    w_polic_regulations = MultiSelectField(
        max_length=200, null=False, choices=MyChoices.polic_CHOICES, verbose_name=_('السياسات واللوائح المكتوبة'))
    org_member_with = models.CharField(max_length=100, null=True, blank=True, choices=MyChoices.bool_CHOICES, verbose_name=_(
        'ھل المؤسسة عضو في اي شبكة او تحالف او جسم تنسیقي؟'))
    coalition_name = models.CharField(
        max_length=255, null=True, blank=True, verbose_name=_('اسم الشبكة / التحالف'))
    coalition_name_en = models.CharField(
        max_length=255, null=True, blank=True, verbose_name=_('اسم الشبكة / التحالف باللغة الانجليزية'))

    publish = models.BooleanField(default=False)

    created_at = models.DateTimeField(auto_now_add=True)
    published_at = models.DateTimeField(blank=True, null=True, default=None)
    updated_at = models.DateTimeField(blank=True, null=True, default=None)

    def __str__(self):
        if self.name:
            # return '%s %s' % (self.name, self.user.username)
            # return '%s' % (self.user.username) + ' / ' + '%s' % (self.name)
            return self.name

    def get_absolute_url(self):
        return reverse("particip_detail", kwargs={"par_id": self.id})
Example #8
0
class EventForm(forms.ModelForm):
    host = forms.ModelChoiceField(
        label="Host",
        required=True,
        help_text=Event._meta.get_field("host").help_text,
        queryset=Organization.objects.all(),
        widget=ModelSelect2Widget(data_view="organization-lookup"),
    )

    administrator = forms.ModelChoiceField(
        label="Administrator",
        required=True,
        help_text=Event._meta.get_field("administrator").help_text,
        queryset=Organization.objects.administrators(),
        widget=ModelSelect2Widget(data_view="administrator-org-lookup"),
    )

    assigned_to = forms.ModelChoiceField(
        label="Assigned to",
        required=False,
        queryset=Person.objects.all(),
        widget=ModelSelect2Widget(data_view="admin-lookup"),
    )

    language = forms.ModelChoiceField(
        label="Language",
        required=False,
        queryset=Language.objects.all(),
        widget=ModelSelect2Widget(data_view="language-lookup"),
    )

    country = CountryField().formfield(
        required=False,
        help_text=Event._meta.get_field("country").help_text,
        widget=Select2Widget,
    )

    comment = MarkdownxFormField(
        label="Comment",
        help_text="Any content in here will be added to comments after this "
        "event is saved.",
        widget=forms.Textarea,
        required=False,
    )

    helper = BootstrapHelper(add_cancel_button=False,
                             duplicate_buttons_on_top=True)

    class Meta:
        model = Event
        fields = [
            "slug",
            "completed",
            "start",
            "end",
            "host",
            "administrator",
            "assigned_to",
            "tags",
            "url",
            "language",
            "reg_key",
            "venue",
            "manual_attendance",
            "contact",
            "country",
            "address",
            "latitude",
            "longitude",
            "open_TTT_applications",
            "curricula",
            "lessons",
            "public_status",
            "comment",
        ]
        widgets = {
            "manual_attendance":
            TextInput,
            "latitude":
            TextInput,
            "longitude":
            TextInput,
            "invoice_status":
            RadioSelect,
            "tags":
            SelectMultiple(attrs={"size": Tag.ITEMS_VISIBLE_IN_SELECT_WIDGET}),
            # "tags": CheckboxSelectMultiple(),
            "curricula":
            CheckboxSelectMultiple(),
            "lessons":
            CheckboxSelectMultiple(),
            "contact":
            Select2TagWidget,
        }

    class Media:
        # thanks to this, {{ form.media }} in the template will generate
        # a <link href=""> (for CSS files) or <script src=""> (for JS files)
        js = (
            "date_yyyymmdd.js",
            "edit_from_url.js",
            "online_country.js",
        )

    def __init__(self, *args, **kwargs):
        show_lessons = kwargs.pop("show_lessons", False)
        add_comment = kwargs.pop("add_comment", True)
        super().__init__(*args, **kwargs)

        self.helper.layout = Layout(
            Field("slug", placeholder="YYYY-MM-DD-location"),
            "completed",
            Field("start", placeholder="YYYY-MM-DD"),
            Field("end", placeholder="YYYY-MM-DD"),
            "host",
            "public_status",
            "administrator",
            "assigned_to",
            "tags",
            "open_TTT_applications",
            "curricula",
            "url",
            "language",
            "reg_key",
            "manual_attendance",
            "contact",
            Div(
                Div(HTML("Location details"), css_class="card-header"),
                Div(
                    "country",
                    "venue",
                    "address",
                    "latitude",
                    "longitude",
                    css_class="card-body",
                ),
                css_class="card mb-2",
            ),
        )

        # if we want to show lessons, we need to alter existing layout
        # otherwise we should remove the field so it doesn't break validation
        if show_lessons:
            self.helper.layout.insert(
                # insert AFTER the curricula
                self.helper.layout.fields.index("curricula") + 1,
                "lessons",
            )
        else:
            del self.fields["lessons"]

        if add_comment:
            self.helper.layout.append("comment")
        else:
            del self.fields["comment"]

    def clean_slug(self):
        # Ensure slug is in "YYYY-MM-DD-location" format
        data = self.cleaned_data["slug"]
        match = re.match(r"(\d{4}|x{4})-(\d{2}|x{2})-(\d{2}|x{2})-.+", data)
        if not match:
            raise ValidationError('Slug must be in "YYYY-MM-DD-location"'
                                  ' format, where "YYYY", "MM", "DD" can'
                                  ' be unspecified (ie. "xx").')
        return data

    def clean_end(self):
        """Ensure end >= start."""
        start = self.cleaned_data["start"]
        end = self.cleaned_data["end"]

        if start and end and end < start:
            raise ValidationError("Must not be earlier than start date.")
        return end

    def clean_open_TTT_applications(self):
        """Ensure there's a TTT tag applied to the event, if the
        `open_TTT_applications` is True."""
        open_TTT_applications = self.cleaned_data["open_TTT_applications"]
        tags = self.cleaned_data.get("tags", None)
        error_msg = "You cannot open applications on a non-TTT event."

        if open_TTT_applications and tags:
            # find TTT tag
            TTT_tag = False
            for tag in tags:
                if tag.name == "TTT":
                    TTT_tag = True
                    break

            if not TTT_tag:
                raise ValidationError(error_msg)

        elif open_TTT_applications:
            raise ValidationError(error_msg)

        return open_TTT_applications

    def clean_curricula(self):
        """Validate tags when some curricula are selected."""
        curricula = self.cleaned_data["curricula"]
        tags = self.cleaned_data["tags"]

        try:
            expected_tags = []
            for c in curricula:
                if c.active and c.carpentry:
                    expected_tags.append(c.carpentry)
                elif c.active and c.mix_match:
                    expected_tags.append("Circuits")
        except (ValueError, TypeError):
            expected_tags = []

        for tag in expected_tags:
            if not tags.filter(name=tag):
                raise forms.ValidationError(
                    "You must add tags corresponding to these curricula.")

        return curricula

    def clean_manual_attendance(self):
        """Regression: #1608 - fix 500 server error when field is cleared."""
        manual_attendance = self.cleaned_data["manual_attendance"] or 0
        return manual_attendance

    def save(self, *args, **kwargs):
        res = super().save(*args, **kwargs)

        comment = self.cleaned_data.get("comment")
        if comment:
            create_comment_signal.send(
                sender=self.__class__,
                content_object=res,
                comment=comment,
                timestamp=None,
            )

        return res
Example #9
0
class GradeSource(models.Model):
    """
    The source of a set of user grades, may be an institution in
    a specific country.  No two institutions in the same country may
    have the same name.
    """
    SCALE_CHOICES = (
        ('DISC', 'Discrete: fixed set of allowed grades'),
        ('CONT', 'Continuous: numeric grade range'),
    )
    STATUS_CHOICES = (
        ('ACTI', 'Active'),
        ('DISA', 'Disabled: invisible to students'),
    )
    country = CountryField()
    institution = models.CharField(max_length=128, verbose_name="Institution/Scale Name")
    config = JSONField(null=False, blank=False, default=dict)
    status = models.CharField(max_length=4, choices=STATUS_CHOICES, default='ACTI')
    scale = models.CharField(max_length=4, choices=SCALE_CHOICES, default='DISC')

    # Only used for Continuous rules
    lower_bound = models.DecimalField(max_digits=8,
                                      decimal_places=2,
                                      default=DECIMAL_ZERO,
                                      help_text="Only used for continuous grade sources")
    upper_bound = models.DecimalField(max_digits=8,
                                      decimal_places=2,
                                      default=DECIMAL_HUNDRED,
                                      help_text="Only used for continuous grade sources")

    def _auto_slug(self):
        return make_slug("%s-%s" % (self.institution, self.country))
    slug = AutoSlugField(populate_from='_auto_slug', null=False, editable=False)

    objects = GradeSourceManager()

    class Meta:
        unique_together = (("country", "institution"),)
        ordering = ('institution', 'country')

    def __str__(self):
        return "%s, %s" % (self.institution, self.country)

    def delete(self):
        raise NotImplementedError("It's a bad thing to delete stuff")

    def get_rule(self, grade):
        """
        Returns the DiscreteRule or ContinuousRule instance that goes with the given grade.
        """
        if self.scale == 'DISC':
            rule = get_object_or_None(self.discrete_rules, lookup_value=grade)
        else:
            # TODO: Make this nicer somehow.
            rules = (self.continuous_rules.filter(lookup_lbound__lte=grade)
                                          .order_by('-lookup_lbound'))
            if rules:
                rule = rules.first()
            else:
                rule = None

        return rule

    def all_discrete_grades(self):
        """
        A all input grades we know about for a discrete scale, sorted in a reasonable way.
        """
        assert self.scale == 'DISC'
        rules = list(DiscreteRule.objects.filter(grade_source=self))
        rules.sort(key=lambda r: r.sortkey(), reverse=True)
        return rules

    def all_discrete_grades_str(self):
        return ', '.join(r.lookup_value for r in self.all_discrete_grades())
Example #10
0
class CourseTeam(models.Model):
    """
    This model represents team related info.

    .. no_pii:
    """
    def __str__(self):
        return "{} in {}".format(self.name, self.course_id)

    def __repr__(self):
        return ("<CourseTeam"
                " id={0.id}"
                " team_id={0.team_id}"
                " team_size={0.team_size}"
                " topic_id={0.topic_id}"
                " course_id={0.course_id}"
                ">").format(self)

    class Meta(object):
        app_label = "teams"

    team_id = models.SlugField(max_length=255, unique=True)
    discussion_topic_id = models.SlugField(max_length=255, unique=True)
    name = models.CharField(max_length=255, db_index=True)
    course_id = CourseKeyField(max_length=255, db_index=True)
    topic_id = models.CharField(default='',
                                max_length=255,
                                db_index=True,
                                blank=True)
    date_created = models.DateTimeField(auto_now_add=True)
    description = models.CharField(max_length=300)
    country = CountryField(default='', blank=True)
    language = LanguageField(
        default='',
        blank=True,
        help_text=ugettext_lazy(
            "Optional language the team uses as ISO 639-1 code."),
    )
    # indexed for ordering
    last_activity_at = models.DateTimeField(default=utc_now, db_index=True)
    users = models.ManyToManyField(User,
                                   db_index=True,
                                   related_name='teams',
                                   through='CourseTeamMembership')
    team_size = models.IntegerField(default=0,
                                    db_index=True)  # indexed for ordering

    field_tracker = FieldTracker()

    # This field would divide the teams into two mutually exclusive groups
    # If the team is org protected, the members in a team is enrolled into a degree bearing institution
    # If the team is not org protected, the members in a team is part of the general edX learning community
    # We need this exclusion for learner privacy protection
    organization_protected = models.BooleanField(default=False)

    # Don't emit changed events when these fields change.
    FIELD_BLACKLIST = ['last_activity_at', 'team_size']

    @classmethod
    def create(cls,
               name,
               course_id,
               description,
               topic_id='',
               country='',
               language='',
               organization_protected=False):
        """Create a complete CourseTeam object.

        Args:
            name (str): The name of the team to be created.
            course_id (str): The ID string of the course associated
              with this team.
            description (str): A description of the team.
            topic_id (str): An optional identifier for the topic the
              team formed around.
            country (str, optional): An optional country where the team
              is based, as ISO 3166-1 code.
            language (str, optional): An optional language which the
              team uses, as ISO 639-1 code.
            organization_protected (bool, optional): specifies whether the team should only
              contain members who are in a organization context, or not

        """
        unique_id = uuid4().hex
        team_id = slugify(name)[0:20] + '-' + unique_id
        discussion_topic_id = unique_id

        course_team = cls(team_id=team_id,
                          discussion_topic_id=discussion_topic_id,
                          name=name,
                          course_id=course_id,
                          topic_id=topic_id,
                          description=description,
                          country=country,
                          language=language,
                          organization_protected=organization_protected)

        return course_team

    def add_user(self, user):
        """Adds the given user to the CourseTeam."""
        from lms.djangoapps.teams.api import user_protection_status_matches_team

        if not CourseEnrollment.is_enrolled(user, self.course_id):
            raise NotEnrolledInCourseForTeam
        if CourseTeamMembership.user_in_team_for_teamset(
                user, self.course_id, self.topic_id):
            raise AlreadyOnTeamInTeamset
        if not user_protection_status_matches_team(user, self):
            raise AddToIncompatibleTeamError
        return CourseTeamMembership.objects.create(user=user, team=self)

    def reset_team_size(self):
        """Reset team_size to reflect the current membership count."""
        self.team_size = CourseTeamMembership.objects.filter(team=self).count()
        self.save()
Example #11
0
class CustomUser(AbstractUser):
    """Reference user model."""

    uuid = models.UUIDField(default=uuid.uuid4,
                            editable=False,
                            verbose_name=_("unique id"))

    pin = models.CharField(
        verbose_name=_("personal identity number"),
        max_length=25,
        unique=True,
        editable=False,
    )

    email = models.EmailField(verbose_name=_("email address"), unique=True)

    full_name = models.CharField(verbose_name=_("full name"), max_length=300)

    picture = models.ImageField(
        verbose_name=_("picture"),
        default="images/default/pic.png",
        upload_to=user_upload_to,
    )

    phone_number = models.CharField(verbose_name=_("phone number"),
                                    max_length=10)

    date_of_birth = models.DateTimeField(verbose_name=_("date of birth"),
                                         null=True)

    born = CountryField(verbose_name=_("born"))

    nationality = CountryField(verbose_name=_("nationality"))

    expired_at = models.DateTimeField(
        verbose_name=_("expired at"),
        default=get_expired_date,
    )

    class Meta:
        """Meta data."""

        verbose_name = _("profile")
        verbose_name_plural = _("profiles")

    def __str__(self: "CustomUser") -> str:
        """It return readable name for the model."""
        return f"{self.username}"

    def age(self: "CustomUser") -> str:
        """A function that display age of user in django admin page."""
        try:
            return f"{timezone.now().year - self.date_of_birth.year}"

        except Exception as error:
            print(error)
            return ""

    def natural_time(self: "CustomUser") -> str:
        """Return natural time for expired date."""
        return naturaltime(self.expired_at)

    def natural_day(self: "CustomUser") -> str:
        """Return natural day for expired date."""
        return naturalday(self.expired_at)

    def has_expired(self: "CustomUser") -> bool:
        """Return bool if user has expired or not."""
        return timezone.now() > self.expired_at

    has_expired.boolean = True
    has_expired.short_description = _("Is Expired ?")
    natural_time.short_description = _("Expired in")
Example #12
0
class Voucher(models.Model):
    type = models.CharField(max_length=20,
                            choices=VoucherType.CHOICES,
                            default=VoucherType.ENTIRE_ORDER)
    name = models.CharField(max_length=255, null=True, blank=True)
    code = models.CharField(max_length=12, unique=True, db_index=True)
    usage_limit = models.PositiveIntegerField(null=True, blank=True)
    used = models.PositiveIntegerField(default=0, editable=False)
    start_date = models.DateTimeField(default=timezone.now)
    end_date = models.DateTimeField(null=True, blank=True)
    # this field indicates if discount should be applied per order or
    # individually to every item
    apply_once_per_order = models.BooleanField(default=False)
    discount_value_type = models.CharField(
        max_length=10,
        choices=DiscountValueType.CHOICES,
        default=DiscountValueType.FIXED,
    )
    discount_value = models.DecimalField(
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES,
    )
    # not mandatory fields, usage depends on type
    countries = CountryField(multiple=True, blank=True)
    min_amount_spent = MoneyField(
        currency=settings.DEFAULT_CURRENCY,
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES,
        null=True,
        blank=True,
    )
    min_checkout_items_quantity = models.PositiveIntegerField(null=True,
                                                              blank=True)
    products = models.ManyToManyField("product.Product", blank=True)
    collections = models.ManyToManyField("product.Collection", blank=True)
    categories = models.ManyToManyField("product.Category", blank=True)

    objects = VoucherQueryset.as_manager()
    translated = TranslationProxy()

    def __str__(self):
        if self.name:
            return self.name
        discount = "%s %s" % (
            self.discount_value,
            self.get_discount_value_type_display(),
        )
        if self.type == VoucherType.SHIPPING:
            if self.is_free:
                return pgettext("Voucher type", "Free shipping")
            return pgettext("Voucher type", "%(discount)s off shipping") % {
                "discount": discount
            }
        if self.type == VoucherType.PRODUCT:
            products = len(self.products.all())
            if products:
                return pgettext(
                    "Voucher type",
                    "%(discount)s off %(product_num)d products") % {
                        "discount": discount,
                        "product_num": products
                    }
        if self.type == VoucherType.COLLECTION:
            collections = len(self.collections.all())
            if collections:
                return pgettext(
                    "Voucher type",
                    "%(discount)s off %(collections_num)d collections") % {
                        "discount": discount,
                        "collections_num": collections
                    }
        if self.type == VoucherType.CATEGORY:
            categories = len(self.categories.all())
            if categories:
                return pgettext(
                    "Voucher type",
                    "%(discount)s off %(categories_num)d categories") % {
                        "discount": discount,
                        "categories_num": categories
                    }
        return pgettext("Voucher type", "%(discount)s off") % {
            "discount": discount
        }

    @property
    def is_free(self):
        return (self.discount_value == Decimal(100)
                and self.discount_value_type == DiscountValueType.PERCENTAGE)

    def get_discount(self):
        if self.discount_value_type == DiscountValueType.FIXED:
            discount_amount = Money(self.discount_value,
                                    settings.DEFAULT_CURRENCY)
            return partial(fixed_discount, discount=discount_amount)
        if self.discount_value_type == DiscountValueType.PERCENTAGE:
            return partial(percentage_discount, percentage=self.discount_value)
        raise NotImplementedError("Unknown discount type")

    def get_discount_amount_for(self, price):
        discount = self.get_discount()
        after_discount = discount(price)
        if after_discount.amount < 0:
            return price
        return price - after_discount

    def validate_min_amount_spent(self, value):
        min_amount_spent = self.min_amount_spent
        if min_amount_spent and value < min_amount_spent:
            msg = pgettext(
                "Voucher not applicable",
                "This offer is only valid for orders over %(amount)s.",
            )
            raise NotApplicable(
                msg % {"amount": amount(min_amount_spent)},
                min_amount_spent=min_amount_spent,
            )

    def validate_min_checkout_items_quantity(self, quantity):
        min_checkout_items_quantity = self.min_checkout_items_quantity
        if min_checkout_items_quantity and min_checkout_items_quantity > quantity:
            msg = pgettext(
                "Voucher not applicable",
                ("This offer is only valid for orders with a minimum of "
                 "%(min_checkout_items_quantity)d quantity."),
            )
            raise NotApplicable(
                msg %
                {"min_checkout_items_quantity": min_checkout_items_quantity},
                min_checkout_items_quantity=min_checkout_items_quantity,
            )
Example #13
0
class UserProfile(models.Model):
	user = models.OneToOneField(User, unique=True, related_name='profile')
	api_token = models.CharField(max_length=24, unique=True)
	registration_ip = models.GenericIPAddressField(blank=True, null=True)
	last_connection_at = models.DateTimeField(auto_now_add=True)
	last_played_server = models.ForeignKey('race.Server', blank=True, null=True,
		related_name='players', on_delete=models.SET_NULL)
	country = CountryField(blank=True)
	points = models.IntegerField(default=0)
	points_history = PickledObjectField(null=True)
	# points snapshot from 4:30 AM, not really precise, but who cares?
	yesterday_points = models.IntegerField(default=0)
	playtime = models.IntegerField(default=0)

	UNKNOWN_GENDER = 1
	MALE_GENDER = 2
	FEMALE_GENDER = 3
	GENDER_CHOICES = (
		(UNKNOWN_GENDER, "Unknown"),
		(MALE_GENDER, "Male"),
		(FEMALE_GENDER, "Female"),
	)
	gender = models.IntegerField(choices=GENDER_CHOICES, default=UNKNOWN_GENDER,
		blank=True)

	has_skin = models.BooleanField(default=False)
	skin_name = models.CharField(max_length=40, blank=True)
	skin_body_color = models.CharField(max_length=7, blank=True)
	skin_feet_color = models.CharField(max_length=7, blank=True)
	skin_body_color_raw = models.IntegerField(max_length=8, blank=True, null=True)
	skin_feet_color_raw = models.IntegerField(max_length=8, blank=True, null=True)

	@property
	def completions(self):
		return Run.objects.filter(user=self.user).count()

	@property
	def runtime(self):
		return Run.objects.filter(user=self.user).aggregate(
			Sum('time')
		)['time__sum']

	@property
	def points_progress(self):
		# points progress since yesterday (actually: since 4:30 AM)
		return "{0:+d}".format(self.points - self.yesterday_points)

	@property
	def position(self):
		if self.points <= 0:
			return None
		return User.objects.exclude(is_active=False) \
			.filter(profile__points__gt=self.points) \
			.order_by('profile__points').count()+1

	def is_female(self):
		return self.gender == self.FEMALE_GENDER

	def map_position(self, map_id):
		# first, retrieve player's map time
		try:
			map_obj = Map.objects.get(pk=map_id)
			player_time = BestRun.objects.get(map=map_obj, user=self.user).time
		except (Map.DoesNotExist, BestRun.DoesNotExist):
			return None
		# now, count all players with lower time and add 1 representing this player
		return BestRun.objects.filter(map=map_obj) \
			.exclude(user__is_active=False) \
			.filter(time__lt=player_time).count() + 1

	def update_playtime(self, seconds):
		if seconds <= 0:
			return
		self.playtime = self.playtime + seconds
		self.save()

	def update_connection(self, server):
		self.last_connection_at = datetime.now()
		self.last_played_server = server
		self.save()

	def is_online(self):
		return self.last_connection_at >= datetime.now()-timedelta(minutes=10)

	def playing_on(self):
		return self.last_played_server if self.is_online() else None

	def best_score(self, map_id):
		try:
			map_obj = Map.objects.get(pk=map_id)
			return BestRun.objects.get(map=map_obj, user=self.user).run
		except (Map.DoesNotExist, BestRun.DoesNotExist):
			return None

	SKIN_LIST = (
		'bluekitty', 'bluestripe', 'brownbear', 'cammo',
		'cammostripes', 'coala', 'default', 'limekitty',
		'pinky', 'redbopp', 'redstripe', 'saddo', 'toptri',
		'twinbop', 'twintri', 'warpaint'
	)

	def get_skin(self):
		if not self.has_skin:
			return None
		return {
			'url': '{0}images/skins/{1}.png'.format(settings.MEDIA_URL, self.skin_name),
			'body_color': self.skin_body_color_raw,
			'feet_color': self.skin_feet_color_raw,
		}

	def regenerate_token(self):
		self.api_token = generate_random_key()
		self.save()

	class Meta:
		get_latest_by = 'user__created_at'
		ordering = ['id']

	def __unicode__(self):
		possessive = '' if self.user.username.endswith('s') else 's'
		return u"{0}'{1} profile".format(self.user.username, possessive)

	def get_absolute_url(self):
		return self.user.get_absolute_url()
Example #14
0
class Tracker(models.Model):
    """Each Tracker object corresponds to a different request sent to the server.
    The amount of parameters logged has to be carefully designed.
    """
    MOBILE = 1
    TABLET = 2
    PC = 3
    BOT = 4
    UNKNOWN = 5

    DEVICE_CHOICES = ((MOBILE, 'mobile'), (TABLET, 'tablet'), (PC, 'pc'),
                      (BOT, 'bot'), (UNKNOWN, 'unknown'))

    timestamp = models.DateTimeField(auto_now_add=False)
    country = CountryField(blank=True)
    region = models.CharField(max_length=255, blank=True)
    city = models.CharField(max_length=255, blank=True)

    type_device = models.IntegerField(choices=DEVICE_CHOICES, default=UNKNOWN)

    operating_system = models.CharField(max_length=255, blank=True)
    device_family = models.CharField(max_length=255, blank=True)
    browser = models.CharField(max_length=255, blank=True)

    # The domain of the request (everything up to the first '/')
    url = models.URLField()
    website = models.ForeignKey(Website,
                                on_delete=models.CASCADE,
                                to_field='website_url',
                                null=True,
                                related_name='trackers')
    # The page visited (everything after the first '/')
    page = models.CharField(max_length=255, blank=True)
    # Query arguments, such as utm_source
    # TODO: Find out what arguments are important for users
    utm_source = models.CharField(max_length=255, null=True, blank=True)

    screen_height = models.IntegerField(null=True, blank=True)
    screen_width = models.IntegerField(null=True, blank=True)

    dnt = models.BooleanField(default=False)

    # The domain of the referrer, everything up to the first '/'
    referrer_url = models.CharField(blank=True, null=True, max_length=255)
    # The page from which the visitor came from. Everything from the first '/'
    referrer_page = models.CharField(max_length=255, blank=True, null=True)

    raw_tracker = models.ForeignKey(RawTracker,
                                    on_delete=models.CASCADE,
                                    null=True,
                                    default=None)

    @classmethod
    def create_from_json(cls, request, data):
        """ This is aimed at data originating from a POST request.

        :param data: Json-formatted information, originating from a javascript installed on
        someone's website.
        """

        # Get the IP address and so the geographical info, if available.
        ip_address = get_real_ip(request) or ''
        user_agent = get_user_agent(request)

        location_data = {}
        # Get location only for non-bots:
        if not user_agent.is_bot:
            if ip_address:
                geo = GeoIP2()
                try:
                    location_data = geo.city(ip_address)
                except GeoIP2Exception:
                    pass

        operating_system = user_agent.os.family
        device_family = user_agent.device.family
        browser = user_agent.browser.family

        if user_agent.is_mobile:
            type_device = cls.MOBILE
        elif user_agent.is_tablet:
            type_device = cls.TABLET
        elif user_agent.is_pc:
            type_device = cls.PC
        elif user_agent.is_bot:
            type_device = cls.BOT
        else:
            type_device = cls.UNKNOWN

        parsed_url = urlparse(data['url']['href'])
        queries = QueryDict(parsed_url.query, mutable=False)

        url = parsed_url.hostname
        page = parsed_url.path
        utm_source = queries.get('utm_source')

        website = None
        website_url = url.lower()
        website_url = website_url.replace('http://',
                                          '').replace('https://',
                                                      '').replace('www.', '')
        if Website.objects.filter(website_url=website_url).exists():
            website = Website.objects.get(website_url=website_url)

        referrer_url = None
        referrer_page = None
        if data.get('referrer'):
            parsed_referrer = urlparse(data['referrer'])
            referrer_url = parsed_referrer.hostname
            referrer_page = parsed_referrer.path

        tracker = cls(
            country=location_data.get('country_code', '') or '',
            region=location_data.get('region', '') or '',
            city=location_data.get('city', '') or '',
            type_device=type_device,
            operating_system=operating_system,
            device_family=device_family,
            browser=browser,
            url=url or '',
            page=page or '',
            utm_source=utm_source,
            website=website,
            screen_width=int(data.get('width', 0)),
            screen_height=int(data.get('height', 0)),
            referrer_url=referrer_url,
            referrer_page=referrer_page,
        )

        return tracker

    def __str__(self):
        return "Tracker {} on page {}{}".format(self.get_type_device_display(),
                                                self.url, self.page)
Example #15
0
class TemplateTranslation(models.Model):
    """
    TemplateTranslation represents a translation for a template and channel pair.
    """

    STATUS_APPROVED = "A"
    STATUS_PENDING = "P"
    STATUS_REJECTED = "R"
    STATUS_UNSUPPORTED_LANGUAGE = "U"

    STATUS_CHOICES = (
        (STATUS_APPROVED, "approved"),
        (STATUS_PENDING, "pending"),
        (STATUS_REJECTED, "rejected"),
        (STATUS_UNSUPPORTED_LANGUAGE, "unsupported_language"),
    )

    # the template this maps to
    template = models.ForeignKey(Template,
                                 on_delete=models.PROTECT,
                                 related_name="translations")

    # the channel that synced this template
    channel = models.ForeignKey(Channel, on_delete=models.PROTECT)

    # the content of this template
    content = models.TextField(null=False)

    # how many variables this template contains
    variable_count = models.IntegerField()

    # the current status of this channel template
    status = models.CharField(max_length=1,
                              choices=STATUS_CHOICES,
                              default=STATUS_PENDING,
                              null=False)

    # the language for this template (ISO639-3 or Facebook code)
    language = models.CharField(max_length=6)

    # the country code for this template
    country = CountryField(null=True, blank=True)

    # the external id for this channel template
    external_id = models.CharField(null=True, max_length=64)

    # whether this channel template is active
    is_active = models.BooleanField(default=True)

    @classmethod
    def trim(cls, channel, existing):
        """
        Trims what channel templates exist for this channel based on the set of templates passed in
        """
        ids = [tc.id for tc in existing]

        # mark any that weren't included as inactive
        TemplateTranslation.objects.filter(channel=channel).exclude(
            id__in=ids).update(is_active=False)

    @classmethod
    def get_or_create(cls, channel, name, language, country, content,
                      variable_count, status, external_id):
        existing = TemplateTranslation.objects.filter(
            channel=channel, external_id=external_id).first()

        if not existing:
            template = Template.objects.filter(org=channel.org,
                                               name=name).first()
            if not template:
                template = Template.objects.create(org=channel.org,
                                                   name=name,
                                                   created_on=timezone.now(),
                                                   modified_on=timezone.now())
            else:
                template.modified_on = timezone.now()
                template.save(update_fields=["modified_on"])

            existing = TemplateTranslation.objects.create(
                template=template,
                channel=channel,
                content=content,
                variable_count=variable_count,
                status=status,
                language=language,
                country=country,
                external_id=external_id,
            )

        else:
            if (existing.status != status or existing.content != content
                    or existing.country != country
                    or existing.language != language):
                existing.status = status
                existing.content = content
                existing.variable_count = variable_count
                existing.is_active = True
                existing.language = language
                existing.country = country
                existing.save(update_fields=[
                    "status", "language", "content", "country", "is_active",
                    "variable_count"
                ])

                existing.template.modified_on = timezone.now()
                existing.template.save(update_fields=["modified_on"])

        return existing

    def __str__(self):
        return f"{self.template.name} ({self.language} [{self.country}]) {self.status}: {self.content}"
Example #16
0
class Phone(TrackingModel, TimeStampedModel):
    PHONE_TYPE_CHOICES = PHONE_TYPE_CHOICES
    MOBILE, HOME, WORK, FAX = 'm', 'h', 'w', 'f'
    profile = models.ForeignKey('hosting.Profile',
                                verbose_name=_("profile"),
                                related_name="phones",
                                on_delete=models.CASCADE)
    number = PhoneNumberField(
        _("number"),
        help_text=
        _("International number format begining with the plus sign (e.g.: +31 10 436 1044)"
          ))
    country = CountryField(_("country"))
    comments = models.CharField(_("comments"), blank=True, max_length=255)
    type = models.CharField(_("phone type"),
                            max_length=3,
                            choices=PHONE_TYPE_CHOICES,
                            default=MOBILE)

    class Meta:
        verbose_name = _("phone")
        verbose_name_plural = _("phones")
        unique_together = ('profile', 'number')

    @property
    def owner(self):
        return self.profile

    @property
    def icon(self):
        if self.type == self.WORK:
            cls = "glyphicon-phone-alt"
        elif self.type == self.MOBILE:
            cls = "glyphicon-phone"
        elif self.type == self.FAX:
            cls = "glyphicon-print"
        else:  # self.HOME or ''
            cls = "glyphicon-earphone"
        title = self.get_type_display().capitalize()
        template = '<span class="glyphicon {cls}" title="{title}" data-toggle="tooltip" data-placement="left"></span>'
        return format_html(template, cls=cls, title=title)

    @property
    def latex(self):
        if self.type == self.WORK:
            icon = r"\faPhoneSquare"
        elif self.type == self.MOBILE:
            icon = r"\faMobilePhone"
        elif self.type == self.FAX:
            icon = r"\faFax"
        else:  # self.HOME or ''
            icon = r"\faPhone"
        return " ".join([icon, self.number.as_international.replace(' ', '~')])

    def __str__(self):
        """ as_e164             '+31104361044'
            as_international    '+31 10 436 1044'
            as_national         '010 436 1044'
            as_rfc3966          'tel:+31-10-436-1044'
        """
        return self.number.as_international

    def __repr__(self):
        return "<{}: {}{} |p#{}>".format(
            self.__class__.__name__,
            "(" + self.type + ") " if self.type else "", self.__str__(),
            self.profile.id)

    def rawdisplay(self):
        t = self.type or "(?)"
        return t + ": " + self.number.as_international
Example #17
0
class Partner(models.Model):
    """
    A partner organization which provides access grants to paywalled resources.
    This model tracks contact information for the partner as well as extra
    information they require on access grant applications.
    """
    class Meta:
        app_label = 'resources'
        verbose_name = 'partner'
        verbose_name_plural = 'partners'
        ordering = ['company_name']

    # --------------------------------------------------------------------------
    # Managers
    # --------------------------------------------------------------------------

    # Define managers. Note that the basic manager must be first to make
    # Django internals work as expected, but we define objects as our custom
    # manager so that we don't inadvertently expose unavailable Partners to
    # end users.
    even_not_available = models.Manager()
    objects = AvailablePartnerManager()

    # --------------------------------------------------------------------------
    # Attributes
    # --------------------------------------------------------------------------

    company_name = models.CharField(
        max_length=40,
        # Translators: In the administrator interface, this text is help text for a field where staff can enter the name of the partner. Don't translate McFarland.
        help_text=_("Partner's name (e.g. McFarland). Note: "
                    "this will be user-visible and *not translated*."))
    date_created = models.DateField(auto_now_add=True)
    coordinator = models.ForeignKey(
        User,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        # Translators: In the administrator interface, this text is help text for a field where staff can specify the username of the account coordinator for this partner.
        help_text=_('The coordinator for this Partner, if any.'))
    featured = models.BooleanField(
        default=False,
        # Translators: In the administrator interface, this text is help text for a check box where staff can select whether a publisher will be featured on the website's front page.
        help_text=_("Mark as true to feature this partner on the front page."))
    company_location = CountryField(
        null=True,
        # Translators: In the administrator interface, this text is help text for a field where staff can enter the partner organisation's country.
        help_text=_("Partner's primary location."))

    # Status metadata
    # --------------------------------------------------------------------------
    # AVAILABLE partners are displayed to users.
    # NOT AVAILABLE partners are only accessible through the admin interface.
    # These may be, e.g., partners TWL used to work with but no longer does
    # (kept in the database for recordkeeping), or they may be partners TWL
    # is setting up a relationship with but isn't ready to expose to public
    # view.
    # We default to NOT_AVAILABLE to avoid inadvertently exposing Partners to
    # the application process when they're not ready yet, and to give staff
    # a chance to build their record incrementally and fix errors.
    AVAILABLE = 0
    NOT_AVAILABLE = 1
    WAITLIST = 2

    STATUS_CHOICES = (
        # Translators: This is a status for a Partner, denoting that editors can apply for access.
        (AVAILABLE, _('Available')),
        # Translators: This is a status for a Partner, denoting that editors cannot apply for access and the Partner will not be displayed to them.
        (NOT_AVAILABLE, _('Not available')),
        # Translators: This is a status for a Partner, denoting that it has no access grants available at this time (but should later).
        (WAITLIST, _('Waitlisted')),
    )

    # Authorization methods, used in both Partner and Stream
    EMAIL = 0
    CODES = 1
    PROXY = 2
    BUNDLE = 3
    LINK = 4

    AUTHORIZATION_METHODS = (
        # Translators: This is the name of the authorization method whereby user accounts are set up by email.
        (EMAIL, _('Email')),
        # Translators: This is the name of the authorization method whereby user accounts are set up via an access code.
        (CODES, _('Access codes')),
        # Translators: This is the name of the authorization method whereby users access resources via an IP proxy.
        (PROXY, _('Proxy')),
        # Translators: This is the name of the authorization method whereby users access resources automatically via the library bundle.
        (BUNDLE, _('Library Bundle')),
        # Translators: This is the name of the authorization method whereby users are provided with a link through which they can create a free account.
        (LINK, _('Link')),
    )

    status = models.IntegerField(
        choices=STATUS_CHOICES,
        default=NOT_AVAILABLE,
        # Translators: In the administrator interface, this text is help text for a field where staff can specify whether this partner should be displayed to users.
        help_text=_('Should this Partner be displayed to users? Is it '
                    'open for applications right now?'))

    renewals_available = models.BooleanField(
        default=False,
        # Translators: In the administrator interface, this text is help text for a field where staff specify whether users can request their account be renewed/extended for this partner.
        help_text=_('Can access grants to this partner be renewed? If so, '
                    'users will be able to request renewals at any time.'))

    accounts_available = models.PositiveSmallIntegerField(
        blank=True,
        null=True,
        # Translators: In the administrator interface, this text is help text for a field where staff specify the total number of available accounts.
        help_text=
        _('Add the number of new accounts to the existing value, not by resetting it to zero. If \'specific stream\' is true, change accounts availability at the collection level.'
          ))

    # Optional resource metadata
    # --------------------------------------------------------------------------
    target_url = models.URLField(
        blank=True,
        null=True,
        # Translators: In the administrator interface, this text is help text for a field where staff can link to a partner's available resources.
        help_text=
        _("Link to partner resources. Required for proxied resources; optional otherwise."
          ))

    terms_of_use = models.URLField(
        blank=True,
        null=True,
        # Translators: In the administrator interface, this text is help text for a field where staff can link to a partner's Terms of Use.
        help_text=_("Link to terms of use. Required if users must agree to "
                    "terms of use to get access; optional otherwise."))

    short_description = models.TextField(
        max_length=1000,
        blank=True,
        null=True,
        # Translators: In the administrator interface, this text is help text for a field where staff can provide a description of a partner's available resources.
        help_text=_("Optional short description of this partner's resources."))

    description = models.TextField(
        "long description",
        blank=True,
        # Translators: In the administrator interface, this text is help text for a field where staff can provide a long description of a partner's available resources.
        help_text=_(
            "Optional detailed description in addition to the short "
            "description such as collections, instructions, notes, special "
            "requirements, alternate access options, unique features, citations notes."
        ))

    send_instructions = models.TextField(
        blank=True,
        null=True,
        # Translators: In the administrator interface, this text is help text for a field where staff can provide instructions to coordinators on sending user data to partners.
        help_text=_("Optional instructions for sending application data to "
                    "this partner."))

    user_instructions = models.TextField(
        blank=True,
        null=True,
        # Translators: In the administrator interface, this text is help text for a field where staff can provide email instructions to editors for accessing a partner resource.
        help_text=_(
            "Optional instructions for editors to use access codes "
            "or free signup URLs for this partner. Sent via email upon "
            "application approval (for links) or access code assignment. "
            "If this partner has collections, fill out user instructions "
            "on each collection instead."))

    excerpt_limit = models.PositiveSmallIntegerField(
        blank=True,
        null=True,
        # Translators: In the administrator interface, this text is help text for a field where staff can optionally provide a excerpt word limit per article.
        help_text=
        _("Optional excerpt limit in terms of number of words per article. Leave empty if no limit."
          ))

    excerpt_limit_percentage = models.PositiveSmallIntegerField(
        blank=True,
        null=True,
        validators=[MaxValueValidator(100)],
        # Translators: In the administrator interface, this text is help text for a field where staff can optionally provide a excerpt word limit per article in terms of percentage per article.
        help_text=
        _("Optional excerpt limit in terms of percentage (%) of an article. Leave empty if no limit."
          ))

    authorization_method = models.IntegerField(
        choices=AUTHORIZATION_METHODS,
        default=EMAIL,
        # Translators: In the administrator interface, this text is help text for a field where staff can specify which method of account distribution this partner uses.
        help_text=
        _("Which authorization method does this partner use? "
          "'Email' means the accounts are set up via email, and is the default. "
          "Select 'Access Codes' if we send individual, or group, login details "
          "or access codes. 'Proxy' means access delivered directly via EZProxy, "
          "and Library Bundle is automated proxy-based access. 'Link' is if we "
          "send users a URL to use to create an account."))

    mutually_exclusive = models.NullBooleanField(
        blank=True,
        null=True,
        default=None,
        # Translators: In the administrator interface, this text is help text for a field where staff can specify whether users can apply for one or multiple collections of resources. Streams means 'collections'.
        help_text=_(
            "If True, users can only apply for one Stream at a time "
            "from this Partner. If False, users can apply for multiple Streams at "
            "a time. This field must be filled in when Partners have multiple "
            "Streams, but may be left blank otherwise."))

    languages = models.ManyToManyField(
        Language,
        blank=True,
        # Translators: In the administrator interface, this text is help text for a field where staff can specify the languages a partner has resources in.
        help_text=_("Select all languages in which this partner publishes "
                    "content."))

    account_length = models.DurationField(
        blank=True,
        null=True,
        # Translators: In the administrator interface, this text is help text for a field where staff can specify the standard duration of a manually granted account for this partner.
        help_text=_(
            "The standard length of an access grant from this Partner. "
            "Entered as &ltdays hours:minutes:seconds&gt."))

    tags = TaggableManager(through=TaggedTextField, blank=True)

    # This field has to stick around until all servers are using the new tags.
    old_tags = TaggableManager(through=None,
                               blank=True,
                               verbose_name=_('Old Tags'))

    # Non-universal form fields
    # --------------------------------------------------------------------------

    # Some fields are required by all resources for all access grants.
    # Some fields are only required by some resources. This is where we track
    # whether *this* resource requires those optional fields.

    registration_url = models.URLField(
        blank=True,
        null=True,
        # Translators: In the administrator interface, this text is help text for a field where staff can link to a partner's registration page.
        help_text=_(
            "Link to registration page. Required if users must sign up "
            "on the partner's website in advance; optional otherwise."))
    real_name = models.BooleanField(
        default=False,
        # Translators: In the administrator interface, this text is help text for a check box where staff can select whether users must specify their real name when applying
        help_text=_('Mark as true if this partner requires applicant names.'))
    country_of_residence = models.BooleanField(
        default=False,
        # Translators: In the administrator interface, this text is help text for a check box where staff can select whether users must specify the country in which they live when applying.
        help_text=_(
            'Mark as true if this partner requires applicant countries '
            'of residence.'))
    specific_title = models.BooleanField(
        default=False,
        # Translators: In the administrator interface, this text is help text for a check box where staff can select whether users must specify a title for the resource they want to access when applying.
        help_text=_('Mark as true if this partner requires applicants to '
                    'specify the title they want to access.'))
    specific_stream = models.BooleanField(
        default=False,
        # Translators: In the administrator interface, this text is help text for a check box where staff can select whether users must specify a collection of resources when applying.
        help_text=_('Mark as true if this partner requires applicants to '
                    'specify the database they want to access.'))
    occupation = models.BooleanField(
        default=False,
        # Translators: In the administrator interface, this text is help text for a check box where staff can select whether users must specify their occupation when applying.
        help_text=_('Mark as true if this partner requires applicants to '
                    'specify their occupation.'))
    affiliation = models.BooleanField(
        default=False,
        # Translators: In the administrator interface, this text is help text for a check box where staff can select whether users must specify their institutional affiliation (e.g. university) when applying.
        help_text=_('Mark as true if this partner requires applicants to '
                    'specify their institutional affiliation.'))
    agreement_with_terms_of_use = models.BooleanField(
        default=False,
        # Translators: In the administrator interface, this text is help text for a check box where staff can select whether users must agree to Terms of Use when applying.
        help_text=_(
            "Mark as true if this partner requires applicants to agree "
            "with the partner's terms of use."))
    account_email = models.BooleanField(
        default=False,
        # Translators: In the administrator interface, this text is help text for a check box where staff can select whether users must first register at the organisation's website before finishing their application.
        help_text=_("Mark as true if this partner requires applicants to have "
                    "already signed up at the partner website."))
    requested_access_duration = models.BooleanField(
        default=False,
        # Translators: In the administrator interface, this text is help text for a check box where staff can select whether users must select the length of account they desire for proxy partners.
        help_text=_(
            "Mark as true if the authorization method of this partner is proxy "
            "and requires the duration of the access (expiry) be specified."))

    def __unicode__(self):
        return self.company_name

    def clean(self):
        if self.agreement_with_terms_of_use and not self.terms_of_use:
            raise ValidationError(
                'When agreement with terms of use is '
                'required, a link to terms of use must be provided.')
        if self.streams.count() > 1:
            if self.mutually_exclusive is None:
                raise ValidationError(
                    'Since this resource has multiple '
                    'Streams, you must specify a value for mutually_exclusive.'
                )
        if self.account_email and not self.registration_url:
            raise ValidationError(
                'When pre-registration is required, '
                'a link to the registration page must be provided.')
        if self.authorization_method == self.PROXY and not self.requested_access_duration:
            raise ValidationError({
                'requested_access_duration': [
                    'When authorization method is proxy, '
                    'requested_access_duration field must be checked.',
                ]
            })

    def get_absolute_url(self):
        return reverse_lazy('partners:detail', kwargs={'pk': self.pk})

    def save(self, *args, **kwargs):
        super(Partner, self).save(*args, **kwargs)
        """Invalidate this partner's pandoc-rendered html from cache"""
        for code in RESOURCE_LANGUAGE_CODES:
            short_description_cache_key = make_template_fragment_key(
                'partner_short_description', [code, self.pk])
            description_cache_key = make_template_fragment_key(
                'partner_description', [code, self.pk])
            send_instructions_cache_key = make_template_fragment_key(
                'partner_send_instructions', [code, self.pk])
            cache.delete(short_description_cache_key)
            cache.delete(description_cache_key)
            cache.delete(send_instructions_cache_key)

    @property
    def get_languages(self):
        return self.languages.all()

    @property
    def is_waitlisted(self):
        return self.status == self.WAITLIST

    @property
    def is_not_available(self):
        return self.status == self.NOT_AVAILABLE
Example #18
0
class Weights(models.Model):
  country = CountryField()
  media_type = models.CharField(max_length=32)
  weight = models.DecimalField(max_digits=4, decimal_places=2)
class Address(models.Model):
    """(Address description)"""

    address = models.TextField(_('address'), blank=True)
    postal_code = models.CharField(_('postal code'),
                                   max_length=16,
                                   null=True,
                                   blank=True)
    city = models.CharField(_('city'), max_length=255, null=True, blank=True)
    country = CountryField(_('country'), null=True, blank=True)
    mappable_location = models.CharField(
        max_length=512,
        blank=True,
        null=True,
        help_text=(
            "This address will be used to calculate latitude and longitude. "
            "Leave blank and set Latitude and Longitude to specify the location yourself, "  # noqa: E501
            "or leave all three blank to auto-fill from the Location field."))
    lat = models.DecimalField(
        max_digits=10,
        decimal_places=7,
        blank=True,
        null=True,
        verbose_name="Latitude",
        help_text="Calculated automatically if mappable location is set.")
    lon = models.DecimalField(
        max_digits=10,
        decimal_places=7,
        blank=True,
        null=True,
        verbose_name="Longitude",
        help_text="Calculated automatically if mappable location is set.")

    def __str__(self):
        return ' '.join((self.address, self.postal_code))

    class Meta:
        abstract = True

    def clean(self):
        """
        Validate set/validate mappable_location, longitude and latitude.
        """
        super(Address, self).clean()

        if self.lat and not self.lon:
            raise ValidationError("Longitude required if specifying latitude.")

        if self.lon and not self.lat:
            raise ValidationError("Latitude required if specifying longitude.")

        if not (self.lat and self.lon) and not self.mappable_location:
            if self.address and self.postal_code and self.city:
                self.mappable_location = self.address.replace("\n", " ")\
                    .replace('\r', ' ') + ", " + self.postal_code + " " + self.city
            else:
                self.mappable_location = None

        # location should always override lat/long if set
        if self.mappable_location and not (self.lat and self.lon):
            try:
                if settings.EVENT_GOOGLE_MAPS_DOMAIN:
                    service = 'googlemaps'
                    geolocator = GoogleV3(
                        api_key=settings.GOOGLE_API_KEY,
                        domain=settings.EVENT_GOOGLE_MAPS_DOMAIN)
                else:
                    service = "openstreetmap"
                    geolocator = Nominatim(user_agent='mezzo')
                mappable_location, (lat, lon) = geolocator.geocode(
                    self.mappable_location)
            except GeocoderQueryError as e:
                raise ValidationError(
                    "The mappable location you specified could not be found on"
                    " {service}: \"{error}\" Try changing the mappable location,"
                    " removing any business names, or leaving mappable location"
                    " blank and using coordinates from getlatlon.com.".format(
                        service=service, error=e.message))
            except ValueError as e:
                raise ValidationError(
                    "The mappable location you specified could not be found on"
                    " {service}: \"{error}\" Try changing the mappable location,"
                    " removing any business names, or leaving mappable location blank"
                    " and using coordinates from getlatlon.com.".format(
                        service=service, error=e.message))
            except TypeError:
                raise ValidationError(
                    "The mappable location you specified could not be found. Try"
                    " changing the mappable location, removing any business names,"
                    " or leaving mappable location blank and using coordinates"
                    " from getlatlon.com.")

            self.mappable_location = mappable_location
            self.lat = lat
            self.lon = lon
Example #20
0
class Voucher(models.Model):
    type = models.CharField(
        max_length=20, choices=VoucherType.CHOICES, default=VoucherType.VALUE)
    name = models.CharField(max_length=255, null=True, blank=True)
    code = models.CharField(max_length=12, unique=True, db_index=True)
    usage_limit = models.PositiveIntegerField(null=True, blank=True)
    used = models.PositiveIntegerField(default=0, editable=False)
    start_date = models.DateField(default=date.today)
    end_date = models.DateField(null=True, blank=True)
    # this field indicates if discount should be applied per order or
    # individually to every item
    apply_once_per_order = models.BooleanField(default=False)
    discount_value_type = models.CharField(
        max_length=10, choices=DiscountValueType.CHOICES,
        default=DiscountValueType.FIXED)
    discount_value = models.DecimalField(
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES)
    # not mandatory fields, usage depends on type
    countries = CountryField(multiple=True, blank=True)
    min_amount_spent = MoneyField(
        currency=settings.DEFAULT_CURRENCY,
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES, null=True, blank=True)
    products = models.ManyToManyField('product.Product', blank=True)
    collections = models.ManyToManyField('product.Collection', blank=True)
    categories = models.ManyToManyField('product.Category', blank=True)
    store = models.ForeignKey(
        Store, null=True, blank=True, related_name='vouchers', on_delete=models.CASCADE)

    objects = VoucherQueryset.as_manager()
    translated = TranslationProxy()

    def __str__(self):
        if self.name:
            return self.name
        discount = '%s %s' % (
            self.discount_value, self.get_discount_value_type_display())
        if self.type == VoucherType.SHIPPING:
            if self.is_free:
                return pgettext('Voucher type', 'Free shipping')
            return pgettext(
                'Voucher type',
                '%(discount)s off shipping') % {'discount': discount}
        if self.type == VoucherType.PRODUCT:
            products = len(self.products.all())
            if products:
                return pgettext(
                    'Voucher type',
                    '%(discount)s off %(product_num)d products') % {
                        'discount': discount,
                        'product_num': products}
        if self.type == VoucherType.COLLECTION:
            collections = len(self.collections.all())
            if collections:
                return pgettext(
                    'Voucher type',
                    '%(discount)s off %(collections_num)d collections') % {
                        'discount': discount,
                        'collections_num': collections}
        if self.type == VoucherType.CATEGORY:
            categories = len(self.categories.all())
            if categories:
                return pgettext(
                    'Voucher type',
                    '%(discount)s off %(categories_num)d categories') % {
                        'discount': discount,
                        'categories_num': categories}
        return pgettext(
            'Voucher type', '%(discount)s off') % {'discount': discount}

    @property
    def is_free(self):
        return (
            self.discount_value == Decimal(100) and
            self.discount_value_type == DiscountValueType.PERCENTAGE)

    def get_discount(self):
        if self.discount_value_type == DiscountValueType.FIXED:
            discount_amount = Money(
                self.discount_value, settings.DEFAULT_CURRENCY)
            return partial(fixed_discount, discount=discount_amount)
        if self.discount_value_type == DiscountValueType.PERCENTAGE:
            return partial(percentage_discount, percentage=self.discount_value)
        raise NotImplementedError('Unknown discount type')

    def get_discount_amount_for(self, price):
        discount = self.get_discount()
        gross_price = price.gross
        gross_after_discount = discount(gross_price)
        if gross_after_discount.amount < 0:
            return gross_price
        return gross_price - gross_after_discount

    def validate_min_amount_spent(self, value):
        min_amount_spent = self.min_amount_spent
        if min_amount_spent and value.gross < min_amount_spent:
            msg = pgettext(
                'Voucher not applicable',
                'This offer is only valid for orders over %(amount)s.')
            raise NotApplicable(
                msg % {'amount': amount(min_amount_spent)},
                min_amount_spent=min_amount_spent)
Example #21
0
class PersFundingOpp(models.Model):
    lang = models.CharField(max_length=100, choices=MyChoices.lang_CHOICES,
                            null=False, blank=False, default='ar', verbose_name=_('اللغة'))
    user = models.ForeignKey(
        User, on_delete=models.CASCADE)
    staff = models.CharField(max_length=100, null=True, blank=True)
    org_name = models.ForeignKey(
        OrgProfile, on_delete=models.CASCADE, null=True, blank=True, verbose_name=_('اسم المنظمة'))

    name_funding = models.CharField(max_length=255, null=True, blank=True,
                                    verbose_name=_("الجهة المانحة"))
    logoo = models.ImageField(upload_to="funding_logos",
                              null=True, blank=True, default='org_logos/default_logo.jpg', verbose_name=_("لوغو الجهة المانحة"))

    category = models.CharField(max_length=100, null=True, blank=True,
                                choices=MyChoices.cat_CHOICES, verbose_name=_('فئة المنحة'))
    fund_type = models.CharField(
        max_length=150, null=False, choices=MyChoices.fund_perso_type_CHOICES, verbose_name=_('نوع المنحة'))

    study_level = models.CharField(max_length=255, null=True, blank=True,
                                   choices=MyChoices.level_study_CHOICES, verbose_name=_('المستوى التعليمي'))
    comp_study = models.CharField(max_length=255, null=True, blank=True,
                                  choices=MyChoices.comp_study_CHOICES, verbose_name=_('الاختصاص التعليمي'))
    domain = models.CharField(max_length=255, null=True, blank=True,
                              choices=MyChoices.domain_CHOICES, verbose_name=_('قطاع المنحة'))

    position_work = CountryField(
        max_length=255, null=False, verbose_name=_("دول المنحة"))
    city_work = models.ForeignKey(
        City, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("المحافظة"))

    fund_org_description = RichTextField(
        max_length=5000, null=False, verbose_name=_("لمحة عن الجهة المانحة"))

    funding_dead_date = models.DateField(
        null=False, verbose_name=_('تاريخ إغلاق المنحة'))
    funding_period = models.CharField(
        max_length=255, choices=MyChoices.period_CHOICES, null=False, verbose_name=_('مدة المنحة'))
    funding_amounte = models.CharField(
        max_length=255, choices=MyChoices.amount_CHOICES, null=False, verbose_name=_('حجم المنحة'))

    # RICHTEXT
    funding_description = RichTextField(
        max_length=5000, null=False, verbose_name=_("وصف المنحة"))
    funding_conditions = RichTextField(
        max_length=5000, null=False, verbose_name=_("شروط المنحة"))
    funding_reqs = RichTextField(
        max_length=5000, null=False, verbose_name=_("متطلبات التقديم"))
    funding_guid = RichTextField(
        max_length=5000, null=False, verbose_name=_("كيفية التقديم"))

    funding_url = models.URLField(
        max_length=255, null=True, blank=True, verbose_name=_('الرابط الأصلي'))

    publish = models.BooleanField(default=False)

    created_at = models.DateTimeField(auto_now_add=True)
    published_at = models.DateTimeField(blank=True, null=True, default=None)
    updated_at = models.DateTimeField(blank=True, null=True, default=None)

    def __str__(self):
        if self.name_funding:
            return self.name_funding
        else:
            return self.org_name

    def get_absolute_url(self):
        return reverse("finance_perso_detail", kwargs={"pk": self.id})
Example #22
0
class SeevcamUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(_('email address'),
                              max_length=254,
                              unique=True,
                              db_index=True)

    first_name = models.CharField(_('first name'),
                                  max_length=30,
                                  blank=False,
                                  null=False)
    last_name = models.CharField(_('last name'),
                                 max_length=30,
                                 blank=False,
                                 null=False)
    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_(
            'Designates whether the user can log into this admin site.'))
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'))
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
    country = CountryField(default='GB')
    timezone = models.CharField(max_length=255,
                                null=False,
                                blank=False,
                                choices=TIMEZONE_CHOICES,
                                default='Europe/London')
    notifications = models.OneToOneField(
        UserNotifications,
        null=False,
        blank=False,
        related_name="user_notification_settings")
    company = models.ForeignKey(Company,
                                null=False,
                                blank=False,
                                related_name="user_company")

    objects = SeevcamUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name', 'last_name']

    class Meta:
        db_table = "users"
        verbose_name = _('user')
        verbose_name_plural = _('users')

    def __str__(self):
        return self.get_full_name()

    def save(self, *args, **kwargs):
        if not self.notifications_id:
            notifications = UserNotifications()
            notifications.save()
            self.notifications = notifications
            self.notifications_id = notifications.id
        super(SeevcamUser, self).save(*args, **kwargs)

    def delete(self, using=None):
        if self.notifications:
            self.notifications.delete()
        super(SeevcamUser, self).delete(using)

    def get_absolute_url(self):
        return "/users/%s/" % urlquote(self.get_full_name())

    def get_full_name(self):
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        return self.first_name

    def email_user(self, subject, message, from_email=None):
        send_mail(subject, message, from_email, [self.email])

    def delete_uploaded_files(self):
        UploadedFile.objects.filter(created_by=self).delete()

    def delete_catalogues(self):
        QuestionCatalogue.objects.filter(catalogue_owner=self).delete()

    def delete_candidates(self):
        Candidate.objects.filter(created_by=self).delete()

    def delete_job_positions(self):
        JobPosition.objects.filter(created_by=self).delete()

    def delete_interviews(self):
        Interview.objects.filter(owner=self, status=Interview.OPEN).delete()

    def delete_reports(self):
        Interview.objects.filter(owner=self, status=Interview.CLOSED).delete()
Example #23
0
class Company(models.Model):
    """
    Core-Model of this project.

    Users can have several companies, companies can buy other companies, create
    order, buy bonds and more.

    The goal is to get more money.

    Each company has a unique ISIN, but we do not use the ISIN as id, tho the id can be gotten from the isin
    """

    objects = CompanyQuerySet.as_manager()

    # null value allowed, since the CentralBank does not have a user account
    # Maybe it would be easier if we just create a user account for the
    # centralbank
    user = models.OneToOneField("users.User",
                                on_delete=models.CASCADE,
                                null=True)

    name = models.CharField(max_length=25, null=False, unique=True)
    isin = models.CharField(max_length=10, unique=True)

    country = CountryField(default="US")

    cash = models.DecimalField(max_digits=25, decimal_places=2, default=0)

    shares = models.PositiveIntegerField(default=1000000)

    class Meta:
        db_table = "company"

    @classmethod
    def get_centralbank(cls) -> Company:
        """Returns the centralbank"""
        return cls.objects.get(name=CENTRALBANK)

    @classmethod
    def get_id_from_isin(cls, isin: str) -> int:
        """Returns the ID from the ISIN"""

        pattern = re.compile("^[A-Z]{2}[0-9]{6}$")
        if not pattern.match(isin):
            logger.info(f"{isin} did not match pattern")
            return -1

        # first 2 chars of the ISIN are the country-code, so we stripe them off
        return int(isin[2:])

    def id_from_isin(self) -> int:
        return self.get_id_from_isin(self.isin)

    @cached_property
    def amount_bonds(self) -> int:
        """Returns the amount of bonds currently in the depot of the company"""
        return self.bond_set.count()

    def enough_money(self, transaction_value) -> bool:
        """Returns True if the company has enough money for the transaction"""
        orders_value = (self.orders_by.add_value().filter(
            typ=Order.type_buy()).aggregate(s=Coalesce(Sum("value"), 0))["s"])
        total = (self.cash - orders_value) - transaction_value
        if total < 0:
            logger.info(
                f"{self} does not have enough money, has: {total}, transaction_value: {transaction_value}"
            )
        return total >= 0

    def bid(self) -> Order:
        """Bid is the price of the highest buy-orders"""

        buy_order = (self.orders_of.filter(
            typ=Order.type_buy()).values("price").annotate(
                total_amount=Sum("amount")).order_by("-price").first())

        return buy_order

    def ask(self) -> Order:
        """Ask is the price of the lowest sell-orders"""

        sell_order = (self.orders_of.filter(
            typ=Order.type_sell()).values("price").annotate(
                total_amount=Sum("amount")).order_by("price").first())

        return sell_order

    def get_absolute_url(self) -> str:
        """Returns the api url for a single company"""
        return reverse("core:company", kwargs={"isin": self.isin})

    def update_activity(self):
        Activity.objects.filter(company=self).update(updated=timezone.now())

    def save(self, *args, **kwargs):
        isin_update = kwargs.pop("isin_update", False)

        if isin_update:
            # when we update the isin
            # we can't use the same approach because otherwise we would change also the id value
            # so we only update the first two chars of the isin-string since they represent the country code
            # and this is the ONLY thing a user can update regarding the isin
            current = self.isin
            self.isin = f"{self.country}{current[2:]}"

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

    def delete(self, using=None, keep_parents=False):

        # When deleting a company, make sure they do not have a depot positions
        # or bonds
        if DepotPosition.objects.filter(depot_of=self).exists():
            raise ValueError(
                "Company still has depot positions. Cannot be deleted!")

        if Bond.objects.filter(company=self).exists():
            raise ValueError("Company still has bonds. Cannot be deleted!")

        trades_count = Trade.objects.filter(
            Q(company=self) | Q(seller=self) | Q(buyer=self)).count()
        trades_history_count = TradeHistory.objects.filter(
            Q(company_name=self.name) | Q(seller_name=self.name)
            | Q(buyer_name=self.name)).count()
        if trades_count != trades_history_count:
            raise ValueError(
                "Trade history has not been implemented for all trades")

        return super().delete(using, keep_parents)

    def __str__(self):
        return self.name
Example #24
0
class Event(models.Model):
    """
    An event.
    """

    SUPPORTED_CURRENCIES = (("EUR", "Euro"),
                            )  # https://stripe.com/docs/currencies

    is_virtual = models.BooleanField(default=False)
    code = models.CharField(max_length=32, unique=True)
    name = models.CharField(max_length=32)
    full_name = models.CharField(max_length=160)
    city = models.CharField(max_length=160)
    country = CountryField()
    presentation = models.TextField(null=True, blank=True)
    website = models.URLField(null=True, blank=True)
    hashtag = models.CharField(max_length=16, null=True, blank=True)
    start_date = models.DateField()
    end_date = models.DateField()
    registration_start_date = models.DateField()
    registration_early_deadline = models.DateTimeField(null=True, blank=True)
    registration_deadline = models.DateTimeField()

    wbs_element = models.CharField(max_length=32, null=True, blank=True)
    ingenico_salt = models.CharField(max_length=200, null=True, blank=True)
    allows_invoices = models.BooleanField(default=True)
    test_mode = models.BooleanField(default=True, editable=False)
    social_event_bundle_fee = models.PositiveSmallIntegerField(default=0)
    signature = models.TextField(null=True, blank=True)

    custom_fields = models.JSONField(default=dict)
    extra_data = models.JSONField(null=True, blank=True, default=dict)

    registrations_count = models.PositiveIntegerField(default=0)

    acl = GenericRelation("evan.Permission")

    objects = EventManager()

    class Meta:
        indexes = [
            models.Index(fields=["code"]),
            models.Index(fields=["start_date", "end_date"]),
        ]

    def __str__(self) -> str:
        return self.name

    def clean(self) -> None:
        validate_event_dates(self)
        if self.hashtag:
            self.hashtag = self.hashtag[1:] if self.hashtag.startswith(
                "#") else self.hashtag

    @property
    def dates_display(self) -> str:
        two_months = self.start_date.month != self.end_date.month
        if two_months:
            return "{0} - {1}".format(date_filter(self.start_date, "F j"),
                                      date_filter(self.end_date, "F j, Y"))
        else:
            return "{0}-{1}".format(date_filter(self.start_date, "F j"),
                                    date_filter(self.end_date, "j, Y"))

    def editable_by_user(self, user) -> bool:
        return self.can_be_managed_by(user)

    def can_be_managed_by(self, user) -> bool:
        return user.is_staff or self.acl.filter(
            user_id=user.id, level__gte=Permission.ADMIN).exists()

    def get_absolute_url(self) -> str:
        return reverse("event:app", args=[self.code])

    def get_registration_url(self) -> str:
        return "".join([
            "https://",
            get_current_site(None).domain,
            reverse("registration:redirect", args=[self.code])
        ])

    @property
    def has_social_event_bundle(self) -> bool:
        return self.social_event_bundle_fee > 0

    @property
    def is_active(self) -> bool:
        return self.start_date <= timezone.now().date() <= self.end_date

    @property
    def is_closed(self) -> bool:
        return timezone.now().date() > self.end_date

    @property
    def is_early(self) -> bool:
        if not self.registration_early_deadline:
            return False
        return timezone.now() <= self.registration_early_deadline

    @property
    def is_open_for_registration(self) -> bool:
        now = timezone.now()
        return self.registration_start_date <= now.date(
        ) and now <= self.registration_deadline

    @cached_property
    def fees_dict(self):
        if not hasattr(self, "_fees"):
            self._fees = {
                (f[0], f[1]): f[2]
                for f in self.fees.values_list("type", "is_early", "value")
            }
        return self._fees

    @cached_property
    def json_badge(self):
        return json.loads(self.badge)
Example #25
0
class PaleoCoreSiteBaseClass(PaleoCoreGeomBaseClass):
    country = CountryField('Country', blank=True, null=True)

    class Meta:
        abstract = True
Example #26
0
class Invoice(models.Model):
    """
    Represents an invoice that is issued because of an order. Because invoices are legally required
    not to change, this object duplicates a lot of data (e.g. the invoice address).

    :param order: The associated order
    :type order: Order
    :param event: The event this belongs to (for convenience)
    :type event: Event
    :param organizer: The organizer this belongs to (redundant, for enforcing uniqueness)
    :type organizer: Organizer
    :param invoice_no: The human-readable, event-unique invoice number
    :type invoice_no: int
    :param is_cancellation: Whether or not this is a cancellation instead of an invoice
    :type is_cancellation: bool
    :param refers: A link to another invoice this invoice refers to, e.g. the canceled invoice in a cancellation
    :type refers: Invoice
    :param invoice_from: The sender address
    :type invoice_from: str
    :param invoice_to: The receiver address
    :type invoice_to: str
    :param full_invoice_no: The full invoice number (for performance reasons only)
    :type full_invoice_no: str
    :param date: The invoice date
    :type date: date
    :param locale: The locale in which the invoice should be printed
    :type locale: str
    :param introductory_text: Introductory text for the invoice, e.g. for a greeting
    :type introductory_text: str
    :param additional_text: Additional text for the invoice
    :type additional_text: str
    :param payment_provider_text: A payment provider specific text
    :type payment_provider_text: str
    :param footer_text: A footer text, displayed smaller and centered on every page
    :type footer_text: str
    :param foreign_currency_display: A different currency that taxes should also be displayed in.
    :type foreign_currency_display: str
    :param foreign_currency_rate: The rate of a foreign currency that the taxes should be displayed in.
    :type foreign_currency_rate: Decimal
    :param foreign_currency_rate_date: The date of the foreign currency exchange rates.
    :type foreign_currency_rate_date: date
    :param file: The filename of the rendered invoice
    :type file: File
    """
    order = models.ForeignKey('Order',
                              related_name='invoices',
                              db_index=True,
                              on_delete=models.CASCADE)
    organizer = models.ForeignKey('Organizer',
                                  related_name='invoices',
                                  db_index=True,
                                  on_delete=models.PROTECT)
    event = models.ForeignKey('Event',
                              related_name='invoices',
                              db_index=True,
                              on_delete=models.CASCADE)
    prefix = models.CharField(max_length=160, db_index=True)
    invoice_no = models.CharField(max_length=19, db_index=True)
    full_invoice_no = models.CharField(max_length=190, db_index=True)
    is_cancellation = models.BooleanField(default=False)
    refers = models.ForeignKey('Invoice',
                               related_name='refered',
                               null=True,
                               blank=True,
                               on_delete=models.CASCADE)
    invoice_from = models.TextField()
    invoice_from_name = models.CharField(max_length=190, null=True)
    invoice_from_zipcode = models.CharField(max_length=190, null=True)
    invoice_from_city = models.CharField(max_length=190, null=True)
    invoice_from_country = CountryField(null=True)
    invoice_from_tax_id = models.CharField(max_length=190, null=True)
    invoice_from_vat_id = models.CharField(max_length=190, null=True)
    invoice_to = models.TextField()
    invoice_to_company = models.TextField(null=True)
    invoice_to_name = models.TextField(null=True)
    invoice_to_street = models.TextField(null=True)
    invoice_to_zipcode = models.CharField(max_length=190, null=True)
    invoice_to_city = models.TextField(null=True)
    invoice_to_state = models.CharField(max_length=190, null=True)
    invoice_to_country = CountryField(null=True)
    invoice_to_vat_id = models.TextField(null=True)
    invoice_to_beneficiary = models.TextField(null=True)
    date = models.DateField(default=today)
    locale = models.CharField(max_length=50, default='en')
    introductory_text = models.TextField(blank=True)
    additional_text = models.TextField(blank=True)
    reverse_charge = models.BooleanField(default=False)
    payment_provider_text = models.TextField(blank=True)
    footer_text = models.TextField(blank=True)
    foreign_currency_display = models.CharField(max_length=50,
                                                null=True,
                                                blank=True)
    foreign_currency_rate = models.DecimalField(decimal_places=4,
                                                max_digits=10,
                                                null=True,
                                                blank=True)
    foreign_currency_rate_date = models.DateField(null=True, blank=True)
    shredded = models.BooleanField(default=False)

    file = models.FileField(null=True,
                            blank=True,
                            upload_to=invoice_filename,
                            max_length=255)
    internal_reference = models.TextField(blank=True)

    objects = ScopedManager(organizer='event__organizer')

    @staticmethod
    def _to_numeric_invoice_number(number):
        return '{:05d}'.format(int(number))

    @property
    def full_invoice_from(self):
        parts = [
            self.invoice_from_name,
            self.invoice_from,
            (self.invoice_from_zipcode or "") + " " +
            (self.invoice_from_city or ""),
            self.invoice_from_country.name
            if self.invoice_from_country else "",
            pgettext("invoice", "VAT-ID: %s") %
            self.invoice_from_vat_id if self.invoice_from_vat_id else "",
            pgettext("invoice", "Tax ID: %s") %
            self.invoice_from_tax_id if self.invoice_from_tax_id else "",
        ]
        return '\n'.join([p.strip() for p in parts if p and p.strip()])

    @property
    def address_invoice_from(self):
        parts = [
            self.invoice_from_name,
            self.invoice_from,
            (self.invoice_from_zipcode or "") + " " +
            (self.invoice_from_city or ""),
            self.invoice_from_country.name
            if self.invoice_from_country else "",
        ]
        return '\n'.join([p.strip() for p in parts if p and p.strip()])

    @property
    def address_invoice_to(self):
        if self.invoice_to and not self.invoice_to_company and not self.invoice_to_name:
            return self.invoice_to

        state_name = ""
        if self.invoice_to_state:
            state_name = self.invoice_to_state
            if str(self.invoice_to_country) in COUNTRIES_WITH_STATE_IN_ADDRESS:
                if COUNTRIES_WITH_STATE_IN_ADDRESS[str(
                        self.invoice_to_country)][1] == 'long':
                    state_name = pycountry.subdivisions.get(
                        code='{}-{}'.format(self.invoice_to_country,
                                            self.invoice_to_state))

        parts = [
            self.invoice_to_company,
            self.invoice_to_name,
            self.invoice_to_street,
            ((self.invoice_to_zipcode or "") + " " +
             (self.invoice_to_city or "") + " " + (state_name or "")).strip(),
            self.invoice_to_country.name if self.invoice_to_country else "",
        ]
        return '\n'.join([p.strip() for p in parts if p and p.strip()])

    def _get_numeric_invoice_number(self):
        numeric_invoices = Invoice.objects.filter(
            event__organizer=self.event.organizer,
            prefix=self.prefix,
        ).exclude(invoice_no__contains='-').annotate(numeric_number=Cast(
            'invoice_no', models.IntegerField())).aggregate(
                max=Max('numeric_number'))['max'] or 0
        return self._to_numeric_invoice_number(numeric_invoices + 1)

    def _get_invoice_number_from_order(self):
        return '{order}-{count}'.format(
            order=self.order.code,
            count=Invoice.objects.filter(event=self.event,
                                         order=self.order).count() + 1,
        )

    def save(self, *args, **kwargs):
        if not self.order:
            raise ValueError('Every invoice needs to be connected to an order')
        if not self.event:
            self.event = self.order.event
        if not self.organizer:
            self.organizer = self.order.event.organizer
        if not self.prefix:
            self.prefix = self.event.settings.invoice_numbers_prefix or (
                self.event.slug.upper() + '-')
            if self.is_cancellation:
                self.prefix = self.event.settings.invoice_numbers_prefix_cancellations or self.prefix
        if not self.invoice_no:
            if self.order.testmode:
                self.prefix += 'TEST-'
            for i in range(10):
                if self.event.settings.get('invoice_numbers_consecutive'):
                    self.invoice_no = self._get_numeric_invoice_number()
                else:
                    self.invoice_no = self._get_invoice_number_from_order()
                try:
                    with transaction.atomic():
                        return super().save(*args, **kwargs)
                except DatabaseError:
                    # Suppress duplicate key errors and try again
                    if i == 9:
                        raise

        self.full_invoice_no = self.prefix + self.invoice_no
        return super().save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        """
        Deleting an Invoice would allow for the creation of another Invoice object
        with the same invoice_no as the deleted one. For various reasons, invoice_no
        should be reliably unique for an event.
        """
        raise Exception(
            'Invoices cannot be deleted, to guarantee uniqueness of Invoice.invoice_no in any event.'
        )

    @property
    def number(self):
        """
        Returns the invoice number in a human-readable string with the event slug prepended.
        """
        return '{prefix}{code}'.format(prefix=self.prefix,
                                       code=self.invoice_no)

    @cached_property
    def canceled(self):
        return self.refered.filter(is_cancellation=True).exists()

    class Meta:
        unique_together = ('organizer', 'prefix', 'invoice_no')
        ordering = (
            'date',
            'invoice_no',
        )

    def __repr__(self):
        return '<Invoice {} / {}>'.format(self.full_invoice_no, self.pk)
Example #27
0
class Order(models.Model):
    order_number = models.CharField(max_length=254, null=False, blank=False)
    user_profile = models.ForeignKey(UserProfile,
                                     on_delete=models.SET_NULL, null=True,
                                     blank=True,
                                     related_name='orders'
                                     )
    date = models.DateTimeField(auto_now_add=True)
    full_name = models.CharField(max_length=50, null=False, blank=False)
    email = models.EmailField(max_length=254, null=False, blank=False)
    phone_number = models.CharField(max_length=20, null=False, blank=False)
    country = CountryField(blank_label='Country *', null=False, blank=False)
    postcode = models.CharField(max_length=20, null=True, blank=True)
    town_or_city = models.CharField(max_length=40, null=False, blank=False)
    street_address1 = models.CharField(
        max_length=80, null=False, blank=False)
    street_address2 = models.CharField(
        max_length=80, null=True, blank=True)
    delivery_cost = models.DecimalField(
        max_digits=6, decimal_places=2, null=False, default=0)
    order_total = models.DecimalField(
        max_digits=10, decimal_places=2, null=False, default=0)
    grand_total = models.DecimalField(
        max_digits=10, decimal_places=2, null=False, default=0)
    original_bag = models.TextField(null=False, blank=False, default='')
    stripe_pid = models.CharField(
        max_length=254, null=False,
        blank=False, default='')

    class Meta:
        ordering = ['-date']

    def _generate_order_number(self):
        """
        Generate unique order number
        """
        return uuid.uuid4().hex.upper()

    def update_total(self):
        """
        Update gran total each time a line item is added
        """
        self.order_total = self.lineitems.aggregate(Sum('lineitem_total'))[
            'lineitem_total__sum'] or 0
        if self.order_total < settings.FREE_DELIVERY_THRESHOLD:
            self.delivery_cost = self.order_total * \
                settings.STANDARD_DELIVERY_PERCENTAGE / 100
        else:
            self.delivery_cost = 0
        self.grand_total = self.order_total + self.delivery_cost
        self.save()

    def save(self, *args, **kwargs):
        """
        Overide the save method to set order number
        if it not been set already
        """
        if not self.order_number:
            self.order_number = self._generate_order_number()
        super().save(*args, **kwargs)

    def __str__(self):
        return self.order_number
Example #28
0
class CourseTeam(models.Model):
    """This model represents team related info."""

    team_id = models.CharField(max_length=255, unique=True)
    discussion_topic_id = models.CharField(max_length=255, unique=True)
    name = models.CharField(max_length=255, db_index=True)
    course_id = CourseKeyField(max_length=255, db_index=True)
    topic_id = models.CharField(max_length=255, db_index=True, blank=True)
    date_created = models.DateTimeField(auto_now_add=True)
    description = models.CharField(max_length=300)
    country = CountryField(blank=True)
    language = LanguageField(
        blank=True,
        help_text=ugettext_lazy(
            "Optional language the team uses as ISO 639-1 code."),
    )
    last_activity_at = models.DateTimeField(
        db_index=True)  # indexed for ordering
    users = models.ManyToManyField(User,
                                   db_index=True,
                                   related_name='teams',
                                   through='CourseTeamMembership')
    team_size = models.IntegerField(default=0,
                                    db_index=True)  # indexed for ordering

    field_tracker = FieldTracker()

    # Don't emit changed events when these fields change.
    FIELD_BLACKLIST = ['last_activity_at', 'team_size']

    @classmethod
    def create(cls,
               name,
               course_id,
               description,
               topic_id=None,
               country=None,
               language=None):
        """Create a complete CourseTeam object.

        Args:
            name (str): The name of the team to be created.
            course_id (str): The ID string of the course associated
              with this team.
            description (str): A description of the team.
            topic_id (str): An optional identifier for the topic the
              team formed around.
            country (str, optional): An optional country where the team
              is based, as ISO 3166-1 code.
            language (str, optional): An optional language which the
              team uses, as ISO 639-1 code.

        """
        unique_id = uuid4().hex
        team_id = slugify(name)[0:20] + '-' + unique_id
        discussion_topic_id = unique_id

        course_team = cls(
            team_id=team_id,
            discussion_topic_id=discussion_topic_id,
            name=name,
            course_id=course_id,
            topic_id=topic_id if topic_id else '',
            description=description,
            country=country if country else '',
            language=language if language else '',
            last_activity_at=datetime.utcnow().replace(tzinfo=pytz.utc))

        return course_team

    def __repr__(self):
        return "<CourseTeam team_id={0.team_id}>".format(self)

    def add_user(self, user):
        """Adds the given user to the CourseTeam."""
        if not CourseEnrollment.is_enrolled(user, self.course_id):
            raise NotEnrolledInCourseForTeam
        if CourseTeamMembership.user_in_team_for_course(user, self.course_id):
            raise AlreadyOnTeamInCourse
        return CourseTeamMembership.objects.create(user=user, team=self)

    def reset_team_size(self):
        """Reset team_size to reflect the current membership count."""
        self.team_size = CourseTeamMembership.objects.filter(team=self).count()
        self.save()
Example #29
0
class Destination_to(models.Model):
    country = CountryField(blank_label='(izberi državo)')
    loading_from = models.CharField(max_length=2, choices=LOADING_TO)
    zip_code = models.IntegerField()
Example #30
0
class NetworkGroup(models.Model):
    objects = NetworkGroupManager()

    GROUP_TYPES = ((0, 'Local group'),
                   (1, 'Chapter'))

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    name = models.CharField(max_length=100)

    group_type = models.IntegerField(default=0, choices=GROUP_TYPES)
    description = models.TextField(blank=True, null=True)

    country = CountryField()
    country_slug = models.SlugField()
    region = models.CharField(max_length=100, blank=True)
    region_slug = models.SlugField(default=None)

    mailinglist_url = models.URLField(blank=True)
    homepage_url = models.URLField(blank=True)
    twitter = models.CharField(max_length=18, blank=True)
    facebook_url = models.URLField(blank=True)
    youtube_url = models.URLField(blank=True)

    position = GeopositionField(blank=True, null=True)

    extra_information = models.TextField(blank=True, null=True)

    members = models.ManyToManyField('Person',
                                     through='NetworkGroupMembership')
    working_groups = models.ManyToManyField('WorkingGroup', blank=True)

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs):
        if self.twitter and self.twitter.startswith(u'@'):
            self.twitter = self.twitter[1:]

        # Slug is either the country slugified or the region
        # Therefore we cannot force slug to be unique
        # (regions might have same name in different countries)
        self.country_slug = slugify(self.get_country_display())
        self.region_slug = slugify(self.region)

        super(NetworkGroup, self).save(*args, **kwargs)

    def get_absolute_url(self):
        # Because reverse can't be smart about conditional parameters
        # we have to have two different urls depending on if it is a
        # country or a region group
        if self.region:
            return reverse('network-region',
                           kwargs={'country': self.country_slug,
                                   'region': self.region_slug})
        else:
            return reverse('network-country',
                           kwargs={'country': self.country_slug})

    class Meta:
        unique_together = ('country', 'region')
        ordering = ('country', 'region')
Example #31
0
class Participant(TimestampMixin, models.Model):
    GENDER_CHOICES = (
        ('M', _('Male')),
        ('F', _('Female')),
    )
    application = models.ForeignKey(Application, blank=True, null=True)
    competition = models.ForeignKey('core.Competition')
    distance = models.ForeignKey('core.Distance', blank=True, null=True)
    price = models.ForeignKey('payment.Price', blank=True, null=True)

    code_short = models.CharField(max_length=20, blank=True, db_index=True)

    company_participant = models.ForeignKey('registration.CompanyParticipant', blank=True, null=True)

    discount_amount = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)

    insurance = models.ForeignKey('core.Insurance', blank=True, null=True)

    team = models.ForeignKey('team.Team', blank=True, null=True)  # If participant is in official team, then this is filled
    team_name = models.CharField(_('Team'), max_length=50, blank=True)  # If participant is not within official team, then he can fill this field
    team_name_slug = models.SlugField(blank=True)

    is_participating = models.BooleanField(_('Is Participating'), default=False)
    is_paying = models.BooleanField(_('Is Paying'), default=True)
    is_competing = models.BooleanField(_('Is Competing'), default=True)  # in case this cyclist is participating for fun, then is_competing should be set to false.
    is_shown_public = models.BooleanField(_('Is Shown Publicly'), default=True)  # if participant doesn't want to be seen publicly in website, this should be set to false.

    first_name = models.CharField(_('First Name'), max_length=60, blank=True)
    last_name = models.CharField(_('Last Name'), max_length=60, blank=True)
    birthday = models.DateField(_('Birthday'), blank=True, null=True)
    is_only_year = models.BooleanField(default=False)
    slug = models.SlugField(blank=True)

    gender = models.CharField(_('Gender'), max_length=1, choices=GENDER_CHOICES, blank=True)

    ssn = models.CharField(_('Social Security Number'), max_length=12, blank=True)
    ssn_hashed = models.CharField(_('Social Security Number Hashed'), max_length=44, blank=True)

    phone_number = models.CharField(_('Phone Number'), max_length=60, blank=True, help_text=_("Result will be sent to this phone number"))
    email = models.EmailField(_('Email'), blank=True)

    send_email = models.BooleanField(_('Send Email'), default=True)
    send_sms = models.BooleanField(_('Send SMS'), default=True)

    country = CountryField(_('Country'), blank=True, null=True)
    city = models.ForeignKey('core.Choices', related_name='+', limit_choices_to={'kind': Choices.KINDS.city}, verbose_name=_('City'), blank=True, null=True)

    bike_brand = models.ForeignKey('core.Choices', related_name='+', limit_choices_to={'kind': Choices.KINDS.bike_brand}, verbose_name=_('Bike Brand'), blank=True, null=True)
    bike_brand2 = models.CharField(_('Bike Brand'), max_length=20, blank=True)

    occupation = models.ForeignKey('core.Choices', related_name='+', limit_choices_to={'kind': Choices.KINDS.occupation}, verbose_name=_('Occupation'), blank=True, null=True)
    where_heard = models.ForeignKey('core.Choices', related_name='+', limit_choices_to={'kind': Choices.KINDS.heard}, verbose_name=_('Where Heard'), blank=True, null=True)

    group = models.CharField(_('Group'), max_length=50, blank=True, help_text=_('Assigned automatically'))  # moved group away from results

    registrant = models.ForeignKey('core.User', blank=True, null=True, verbose_name=_('Registrant'))
    legacy_id = models.IntegerField(blank=True, null=True)

    full_name = models.CharField(_('Full Name'), max_length=120, blank=True)

    primary_number = models.ForeignKey('registration.Number', blank=True, null=True)

    is_temporary = models.BooleanField(default=False)
    is_sent_number_email = models.BooleanField(default=False)
    is_sent_number_sms = models.BooleanField(default=False)

    registration_dt = models.DateTimeField(_("Registration Date"), blank=True, null=True)

    comment = models.TextField(blank=True)

    total_entry_fee = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)
    total_insurance_fee = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)
    final_price = models.DecimalField(max_digits=20, decimal_places=2, default=0.0)

    survey_answer1 = models.CharField(max_length=20, blank=True)

    _competition_class = None

    class Meta:
        ordering = ('distance', 'created')
        verbose_name = _('participant')
        verbose_name_plural = _('participants')

    def __str__(self):
        return '%s %s - %s %s' % (self.first_name, self.last_name, self.competition, self.distance)

    def get_competition_class(self):
        if not self._competition_class:
            class_ = load_class(self.competition.processing_class)
            self._competition_class = class_(self.competition.id)
        return self._competition_class

    def set_slug(self):
        if self.birthday:
            try:
                self.slug = CustomSlug.objects.get(first_name=self.first_name, last_name=self.last_name, birthday=self.birthday).slug
            except CustomSlug.DoesNotExist:
                self.slug = slugify('%s-%s-%i' % (self.first_name, self.last_name, self.birthday.year), only_ascii=True)
        else:
            self.slug = slugify('%s-%s' % (self.first_name, self.last_name), only_ascii=True)

    def set_group(self):
        if not self.group and self.is_participating:
            self.group = self.get_competition_class().assign_group(self.distance_id, self.gender, self.birthday, participant=self)

    def save(self, *args, **kwargs):
        prev_participant = None
        if self.pk:
            prev_participant = Participant.objects.get(id=self.pk)

        self.full_name = '%s %s' % (self.first_name, self.last_name)  # Full name always should be based on first_name and last_name fields

        self.team_name_slug = slugify(self.team_name.replace(' ', ''))

        # In case first name, last name or birthday is changed, then slug changes as well.
        old_slug = self.slug
        self.set_slug()
        if old_slug != self.slug and self.is_participating:
            Log.objects.create(content_object=self, action="Changed SLUG", message="Changing on number model", params={'old_slug':old_slug, 'new_slug': self.slug})
            self.numbers(slug=old_slug).update(participant_slug=self.slug)  # We update number slugs to match new slug

            if self.team:
                team = self.team
                self.team = None  # Remove connection to existing team member.

                # Because of slug change connection to team is disconnected.
                # After disconnection team points are recalculated on every stage team have participated.
                try:
                    member = team.member_set.get(slug=old_slug)
                    for appl in member.memberapplication_set.all():
                        class_ = load_class(appl.competition.processing_class)
                        processing_class = class_(competition=appl.competition)
                        processing_class.recalculate_team_result(team=team)
                        appl.participant = None
                        appl.participant_unpaid = None
                        appl.participant_potential = None
                        appl.save()
                except:
                    pass

            sebs = self.primary_sebstandings_set.filter(competition_id__in=self.competition.get_ids())
            if sebs:
                sebs.update(participant_slug=self.slug)
                # Check if there are double standings that should be merged
                from velo.results.tasks import merge_standings
                merge_standings.delay(self.competition.parent_id if self.competition.level == 2 else self.competition_id)

        self.set_group()

        if not self.primary_number and self.is_participating:
            numbers = self.numbers().order_by('-number')
            if numbers:
                self.primary_number = numbers[0]

        if not self.registration_dt:
            self.registration_dt = timezone.now()

        # Recalculate totals. # TODO: This should be done when creating payment, not on any save.
        recalculate_participant(self, commit=False)

        obj = super(Participant, self).save(*args, **kwargs)

        if not self.code_short and self.id:
            hashids = Hashids(salt=settings.SECRET_KEY, min_length=7, alphabet=settings.HASH_ALPHABET)
            self.code_short = hashids.encode(self.id)
            self.save()

        if self.is_participating and (not prev_participant or not prev_participant.is_participating):
            from velo.results.tasks import master_update_helper_result_table
            master_update_helper_result_table.delay(participant_id=self.id)

        if old_slug != self.slug and self.is_participating:
            from velo.team.utils import match_participant_to_applied
            match_participant_to_applied(self)

        return obj

    def numbers(self, slug=None):
        self.set_group()
        if not slug:
            slug = self.slug
        group = self.get_competition_class().get_group_for_number_search(self.distance_id, self.gender, self.birthday)
        number_queryset = Number.objects.filter(participant_slug=slug, competition_id__in=self.competition.get_ids(), distance=self.distance, group=group)

        return number_queryset
Example #32
0
class Place(TrackingModel, TimeStampedModel):
    owner = models.ForeignKey('hosting.Profile',
                              verbose_name=_("owner"),
                              related_name="owned_places",
                              on_delete=models.CASCADE)
    address = models.TextField(_("address"),
                               blank=True,
                               help_text=_("e.g.: Nieuwe Binnenweg 176"))
    city = models.CharField(
        _("city"),
        blank=True,
        max_length=255,
        validators=[validate_not_all_caps, validate_not_too_many_caps],
        help_text=_(
            "Name in the official language, not in Esperanto (e.g.: Rotterdam)"
        ))
    closest_city = models.CharField(
        _("closest big city"),
        blank=True,
        max_length=255,
        validators=[validate_not_all_caps, validate_not_too_many_caps],
        help_text=_("If your place is in a town near a bigger city. "
                    "Name in the official language, not in Esperanto."))
    postcode = models.CharField(_("postcode"), blank=True, max_length=11)
    state_province = models.CharField(_("State / Province"),
                                      blank=True,
                                      max_length=70)
    country = CountryField(_("country"))
    latitude = models.FloatField(_("latitude"), null=True, blank=True)
    longitude = models.FloatField(_("longitude"), null=True, blank=True)
    max_guest = models.PositiveSmallIntegerField(_("maximum number of guest"),
                                                 null=True,
                                                 blank=True)
    max_night = models.PositiveSmallIntegerField(_("maximum number of night"),
                                                 null=True,
                                                 blank=True)
    contact_before = models.PositiveSmallIntegerField(
        _("contact before"),
        null=True,
        blank=True,
        help_text=_("Number of days before people should contact host."))
    description = models.TextField(
        _("description"),
        blank=True,
        help_text=_("Description or remarks about your place."))
    short_description = models.CharField(
        _("short description"),
        blank=True,
        max_length=140,
        help_text=_(
            "Used in the book and on profile, 140 characters maximum."))
    available = models.BooleanField(
        _("available"),
        default=True,
        help_text=
        _("If this place is searchable. If yes, you will be considered as host."
          ))
    in_book = models.BooleanField(
        _("print in book"),
        default=True,
        help_text=
        _("If you want this place to be in the printed book. Must be available."
          ))
    tour_guide = models.BooleanField(
        _("tour guide"),
        default=False,
        help_text=_("If you are ready to show your area to visitors."))
    have_a_drink = models.BooleanField(
        _("have a drink"),
        default=False,
        help_text=_(
            "If you are ready to have a coffee or beer with visitors."))
    sporadic_presence = models.BooleanField(
        _("irregularly present"),
        default=False,
        help_text=
        _("If you are not often at this address and need an advance notification."
          ))
    conditions = models.ManyToManyField('hosting.Condition',
                                        verbose_name=_("conditions"),
                                        blank=True)
    family_members = models.ManyToManyField('hosting.Profile',
                                            verbose_name=_("family members"),
                                            blank=True)
    authorized_users = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        verbose_name=_("authorized users"),
        blank=True,
        help_text=
        _("List of users authorized to view most of data of this accommodation."
          ))

    available_objects = AvailableManager()
    with_coord = WithCoordManager()

    class Meta:
        verbose_name = _("place")
        verbose_name_plural = _("places")
        default_manager_name = 'all_objects'

    @property
    def profile(self):
        """Proxy for self.owner. Rename 'owner' to 'profile' if/as possible."""
        return self.owner

    @property
    def bbox(self):
        """Return an OpenStreetMap formated bounding box.
        See http://wiki.osm.org/wiki/Bounding_Box
        """
        dx, dy = 0.007, 0.003  # Delta lng and delta lat around position
        lat, lng = self.latitude, self.longitude
        boundingbox = (lng - dx, lat - dy, lng + dx, lat + dy)
        return ",".join([str(coord) for coord in boundingbox])

    @property
    def any_accommodation_details(self):
        return any([
            self.description, self.contact_before, self.max_guest,
            self.max_night
        ])

    @property
    def owner_available(self):
        return self.tour_guide or self.have_a_drink

    @property
    def supervised_by(self):
        group = Group.objects.get(name=self.country.code)
        return [user.profile for user in group.user_set.all()]

    def get_absolute_url(self):
        return reverse('place_detail', kwargs={'pk': self.pk})

    def __str__(self):
        return ", ".join([self.city, force_text(self.country.name)
                          ]) if self.city else force_text(self.country.name)

    def __repr__(self):
        return "<{} #{}: {}>".format(self.__class__.__name__, self.id,
                                     self.__str__())

    def rawdisplay_family_members(self):
        family_members = self.family_members.exclude(
            pk=self.owner.pk).order_by('birth_date')
        return ", ".join(fm.rawdisplay() for fm in family_members)

    def rawdisplay_conditions(self):
        return ", ".join(c.__str__() for c in self.conditions.all())

    def display_conditions_latex(self):
        return r"\, ".join(c.latex for c in self.conditions.all())