class Order(models.Model):
    ORDER_STATUS_OPENED = 1
    ORDER_STATUS_CLOSED = 2
    ORDER_STATUS_CHOICES = (
        (ORDER_STATUS_OPENED, 'Opened'),
        (ORDER_STATUS_CLOSED, 'Closed'),
    )

    ORDER_TYPE_BUY = 1
    ORDER_TYPE_SELL = 2
    ORDER_TYPE_CHOICES = (
        (ORDER_TYPE_BUY, 'buy'),
        (ORDER_TYPE_SELL, 'sell'),
    )

    user = models.ForeignKey(SystemUser)
    currency_pair = models.ForeignKey(CurrencyPair)
    status = models.SmallIntegerField(choices=ORDER_STATUS_CHOICES)
    type = models.SmallIntegerField(choices=ORDER_TYPE_CHOICES)
    start_time = models.DateTimeField(auto_now_add=True)
    start_value = models.ForeignKey(CurrencyPairValue, related_name='start_value')
    end_time = models.DateTimeField(null=True)
    end_value = models.ForeignKey(CurrencyPairValue, related_name='end_value', null=True)
    amount = models.FloatField(validators=[validators.MinValueValidator(0.01)])
    end_profit = models.FloatField(null=True, blank=True)

    def close(self):
        self.end_time = timezone.now()
        end_value = CurrencyPairValue.objects.filter(currency_pair=self.currency_pair).first()
        self.end_value = end_value
        self.status = Order.ORDER_STATUS_CLOSED
        with transaction.atomic():
            amount = self.get_profit(end_value)
            self.user.account.change_amount_after_order(amount, raise_exception=False)
            self.end_profit = amount
            self.save()

    def get_profit(self, currency_pair_value):
        amount = self.amount
        if self.type == self.ORDER_TYPE_BUY:
            debt_amount = amount * self.start_value.ask
            amount -= debt_amount / currency_pair_value.bid
            rest_currency = self.currency_pair.base_currency
        else:
            amount *= (self.start_value.bid - currency_pair_value.ask)
            rest_currency = self.currency_pair.quoted_currency
        user_currency = self.user.account.currency
        if rest_currency != user_currency:
            sub_pair = CurrencyPair.objects.get(
                Q(base_currency=rest_currency,
                  quoted_currency=user_currency) |
                Q(base_currency=user_currency,
                  quoted_currency=rest_currency)
            )
            sub_pair = CurrencyPairValue.objects.filter(currency_pair=sub_pair).first()
            if sub_pair.currency_pair.base_currency == rest_currency:
                amount *= sub_pair.bid
            else:
                amount /= sub_pair.ask
        return amount

    def __unicode__(self):
        return '%s [%s]: %s' % (self.get_type_display(), self.get_status_display(), self.start_time)
Exemple #2
0
class SupportContract(Contract):
    """Support contract model."""

    FIXED_FEE_PERIOD = Choices(
        ('DAILY', _('Daily')),
        ('WEEKLY', _('Weekly')),
        ('MONTHLY', _('Monthly')),
        ('YEARLY', _('Yearly')),
    )

    day_rate = models.DecimalField(blank=True,
                                   null=True,
                                   max_digits=6,
                                   decimal_places=2,
                                   default=0.00,
                                   validators=[
                                       validators.MinValueValidator(0),
                                       validators.MaxValueValidator(9999),
                                   ])
    fixed_fee = models.DecimalField(blank=True,
                                    null=True,
                                    max_digits=9,
                                    decimal_places=2,
                                    default=0.00,
                                    validators=[
                                        validators.MinValueValidator(0),
                                        validators.MaxValueValidator(9999999),
                                    ])
    fixed_fee_period = models.CharField(blank=True,
                                        null=True,
                                        max_length=10,
                                        choices=FIXED_FEE_PERIOD)
    starts_at = models.DateField()
    ends_at = models.DateField(blank=True, null=True)

    @classmethod
    def perform_additional_validation(cls, data, instance=None):
        """Perform additional validation on the object."""
        instance_id = instance.id if instance else None  # noqa
        starts_at = data.get('starts_at', getattr(instance, 'starts_at', None))
        ends_at = data.get('ends_at', getattr(instance, 'ends_at', None))
        day_rate = data.get('day_rate', getattr(instance, 'day_rate', None))
        fixed_fee = data.get('fixed_fee', getattr(instance, 'fixed_fee', None))
        fixed_fee_period = data.get(
            'fixed_fee_period', getattr(instance, 'fixed_fee_period', None))

        if starts_at and ends_at:
            # Verify whether the start date of the contract comes before the end date
            if starts_at >= ends_at:
                raise ValidationError(
                    _('The start date should be set before the end date'), )

        # Ensure we have either a day rate or a fixed fee + period, but never both
        if day_rate:
            if fixed_fee:
                raise ValidationError(
                    _('A contract can not have both a fixed fee and a day rate'
                      ), )
        elif fixed_fee:
            if not fixed_fee_period:
                raise ValidationError(
                    _('A contract with a fixed fee requires a fixed fee period'
                      ), )
        else:
            raise ValidationError(
                _('A contract should have either a fixed fee or a day rate'), )

    def get_validation_args(self):
        """Get a dict used for validation based on this instance."""
        return {
            'starts_at': getattr(self, 'starts_at', None),
            'ends_at': getattr(self, 'ends_at', None),
            'day_rate': getattr(self, 'day_rate', None),
            'fixed_fee': getattr(self, 'fixed_fee', None),
            'fixed_fee_period': getattr(self, 'fixed_fee_period', None),
        }
Exemple #3
0
class Application(models.Model):
    GENDER_CHOICES = [
        (None, ""),
        ("male", "Male"),
        ("female", "Female"),
        ("non-binary", "Non-binary"),
        ("other", "Other"),
        ("no-answer", "Prefer not to answer"),
    ]

    ETHNICITY_CHOICES = [
        (None, ""),
        ("american-native", "American Indian or Alaskan Native"),
        ("asian-pacific-islander", "Asian / Pacific Islander"),
        ("black-african-american", "Black or African American"),
        ("hispanic", "Hispanic"),
        ("caucasian", "White / Caucasian"),
        ("other", "Multiple ethnicity / Other"),
        ("no-answer", "Prefer not to answer"),
    ]

    STUDY_LEVEL_CHOICES = [
        (None, ""),
        ("undergraduate", "Undergraduate"),
        ("gradschool", "Graduate School"),
    ]

    user = models.OneToOneField(User, on_delete=models.CASCADE, null=False)
    team = models.ForeignKey(
        Team, related_name="applications", on_delete=models.CASCADE, null=False
    )

    # User Submitted Fields
    birthday = models.DateField(
        null=False,
        validators=[
            validators.MaxValueValidator(
                date(2003, 2, 6),
                message="You must be over 18 years old on February 6, 2021 to participate in MakeUofT.",
            )
        ],
    )
    gender = models.CharField(max_length=50, choices=GENDER_CHOICES, null=False)
    ethnicity = models.CharField(max_length=50, choices=ETHNICITY_CHOICES, null=False)
    school = models.CharField(help_text="University", max_length=255, null=False,)
    study_level = models.CharField(
        max_length=50, choices=STUDY_LEVEL_CHOICES, null=False
    )
    graduation_year = models.IntegerField(
        null=False,
        validators=[
            validators.MinValueValidator(
                2000, message="Enter a realistic graduation year."
            ),
            validators.MaxValueValidator(
                2030, message="Enter a realistic graduation year."
            ),
        ],
    )
    resume = models.FileField(
        upload_to="applications/resumes/",
        validators=[
            UploadedFileValidator(
                content_types=["application/pdf"], max_upload_size=20 * 1024 * 1024
            )
        ],
        null=False,
    )
    resume_sharing = models.BooleanField(
        help_text="I consent to IEEE UofT sharing my resume with event sponsors (optional).",
        default=False,
        null=False,
    )
    eligibility_agree = models.BooleanField(
        help_text="I confirm that I will be over 18 years old and a university student "
        "on February 6, 2021.",
        blank=False,
        null=False,
    )
    conduct_agree = models.BooleanField(
        help_text="I have read and agree to the "
        '<a href="https://docs.google.com/document/d/1RH36R1nt8KQfKtd2YoNAJNuaCW5um55a6oVP_bWRK6U/edit" target="_blank">code of conduct</a>.',
        blank=False,
        null=False,
    )
    data_agree = models.BooleanField(
        help_text="I consent to have the data in this application collected for event purposes "
        "including administration, ranking, and event communication.",
        blank=False,
        null=False,
    )

    rsvp = models.BooleanField(null=True)
    created_at = models.DateTimeField(auto_now_add=True, null=False)
    updated_at = models.DateTimeField(auto_now=True, null=False)

    def __str__(self):
        return f"{self.user.first_name} {self.user.last_name}"
Exemple #4
0
class MRSRequest(models.Model):
    SESSION_KEY = 'MRSRequest.ids'

    STATUS_NEW = 1  # matches admin.models.ADDITION
    # Those have status different from admin flags
    STATUS_CANCELED = 100
    STATUS_REJECTED = 999
    STATUS_INPROGRESS = 1000
    STATUS_VALIDATED = 2000

    STATUS_CHOICES = (
        (STATUS_NEW, 'Soumise'),
        (STATUS_CANCELED, 'Annulée'),
        (STATUS_REJECTED, 'Rejetée'),
        (STATUS_INPROGRESS, 'En cours de liquidation'),
        (STATUS_VALIDATED, 'Validée'),
    )

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    creation_datetime = models.DateTimeField(
        default=timezone.now,
        db_index=True,
        verbose_name='Date et heure de la demande',
    )
    display_id = models.BigIntegerField(
        verbose_name='Numéro de demande',
        unique=True,
    )
    caisse = models.ForeignKey(
        'caisse.Caisse',
        on_delete=models.SET_NULL,
        null=True,
    )
    insured = models.ForeignKey(
        'person.Person',
        on_delete=models.SET_NULL,
        null=True,
    )
    insured_shift = models.NullBooleanField(
        default=None,
        null=True,
        blank=True,
        verbose_name='Assuré a basculé sur cette demande',
    )
    modevp = models.BooleanField(
        default=False,
        blank=True,
        verbose_name='Avez vous voyagé en véhicule personnel ?',
        help_text='(Voiture, moto)',
    )
    distancevp = models.PositiveIntegerField(
        verbose_name='Distance (km)',
        help_text='Indiquez le nombre total de kilomètres parcourus :'
        ' Par exemple, vous réalisez 2 trajets de 40 kilomètres'
        ' aller/retour : déclarez 80 kilomètres parcourus.',
        null=True,
        blank=True,
    )
    expensevp_toll = models.DecimalField(
        null=True,
        blank=True,
        decimal_places=2,
        max_digits=6,
        validators=[validators.MinValueValidator(Decimal('0.00'))],
        verbose_name='Frais de péage',
        help_text='Somme totale des frais de péage (en € TTC)',
    )
    expensevp_parking = models.DecimalField(
        null=True,
        blank=True,
        decimal_places=2,
        max_digits=6,
        validators=[validators.MinValueValidator(Decimal('0.00'))],
        verbose_name='Frais de stationnement',
        help_text='Somme totale des frais de stationnement (en € TTC)',
    )

    @denormalized(
        models.DecimalField,
        blank=True,
        null=True,
        decimal_places=2,
        max_digits=6,
        validators=[validators.MinValueValidator(Decimal('0.00'))],
        verbose_name='Total des frais',
        help_text='Somme des frais de péage et stationnement (en € TTC)'
    )
    def expensevp(self):
        if self.expensevp:
            return self.expensevp

        expensevp_parking = self.expensevp_parking or 0
        expensevp_toll = self.expensevp_toll or 0
        return expensevp_parking + expensevp_toll

    modeatp = models.BooleanField(
        blank=True,
        default=False,
        verbose_name='Avez vous voyagé en transports en commun ?',
        help_text='(Avion, bus, métro, train, bateau…)',
    )
    expenseatp = models.DecimalField(
        blank=True,
        null=True,
        decimal_places=2, max_digits=6,
        default=0,
        validators=[validators.MinValueValidator(Decimal('0.00'))],
        verbose_name='Frais de transports',
        help_text=(
            'Somme totale des frais de'
            ' transport en commun (en € TTC)'
        )
    )
    pel = models.CharField(
        max_length=14,
        verbose_name='Numéro de PMET',
        null=True,
        blank=True,
        validators=[
            validators.RegexValidator(
                '[a-zA-Z0-9]{14}',
                message='Le numéro de PMET doit comporter'
                ' 14 caractères alpha numériques',
            )
        ],
    )
    status = models.IntegerField(
        choices=STATUS_CHOICES,
        verbose_name='Statut',
        default=STATUS_NEW,
    )
    status_datetime = models.DateTimeField(
        db_index=True,
        null=True,
        blank=True,
        verbose_name='Date et heure de changement de statut',
    )
    status_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        db_index=True,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        verbose_name='Auteur du changement de statut',
    )
    suspended = models.BooleanField(
        db_index=True,
        blank=True,
        default=False
    )
    institution = models.ForeignKey(
        'institution.Institution',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        verbose_name='Établissement',
    )
    mandate_datevp = models.DateField(
        null=True,
        blank=True,
        verbose_name='Date de mandatement VP',
        validators=[
            validators.MinValueValidator(
                datetime.date(year=2000, month=1, day=1)
            )
        ],
    )
    mandate_dateatp = models.DateField(
        null=True,
        blank=True,
        verbose_name='Date de mandatement ATP',
        validators=[
            validators.MinValueValidator(
                datetime.date(year=2000, month=1, day=1)
            )
        ],
    )
    payment_base = models.DecimalField(
        null=True,
        blank=True,
        max_digits=8,
        decimal_places=2,
        verbose_name='Base de remboursement',
    )
    payment_amount = models.DecimalField(
        null=True,
        blank=True,
        max_digits=8,
        decimal_places=2,
        verbose_name='Montant remboursé',
    )
    adeli = models.IntegerField(null=True, blank=True)
    data = JSONField(
        blank=True,
        encoder=DjangoJSONEncoder,
        null=True,
        verbose_name='Formulaire tel que soumit par l\'usager',
    )
    conflicts_accepted = models.PositiveIntegerField(
        default=0,
        verbose_name='Nb. signalements acceptés',
        help_text='Nombre de signalements acceptés pour cette demande',
    )
    conflicts_resolved = models.PositiveIntegerField(
        default=0,
        verbose_name='Nb. signalements résolus',
        help_text='Nombre de signalements résolus avant soumission',
    )
    token = models.CharField(
        default=secrets.token_urlsafe,
        null=True,
        editable=False,
        verbose_name='Token d\'authentification pour modifier la demande',
        max_length=255,
    )

    objects = MRSRequestManager()

    class Meta:
        verbose_name = 'Demande'
        ordering = ['-creation_datetime']

    @property
    def dates(self):
        if getattr(self, '_dates', None) is None:
            self._dates = set()
            for transport in self.transport_set.all():
                for date in transport.dates:
                    self._dates.add(date)
            self._dates = sorted(self._dates)
        return self._dates

    @property
    def duplicate_transports(self):
        if getattr(self, '_duplicate_transports', None) is None:
            self._duplicate_transports = Transport.objects.filter(
                mrsrequest__insured=self.insured,
                mrsrequest__status__in=(
                    self.STATUS_INPROGRESS,
                    self.STATUS_VALIDATED,
                ),
            ).exclude(
                models.Q(mrsrequest__pk=self.pk)
            ).filter(
                models.Q(date_depart__in=self.dates)
                | models.Q(date_return__in=self.dates)
            ).distinct().order_by(
                'mrsrequest__creation_datetime'
            ).select_related(
                'mrsrequest'
            ).prefetch_related('mrsrequest__transport_set')
        return self._duplicate_transports

    @property
    def duplicates_by_transport(self):
        if getattr(self, '_duplicates_dates', None) is None:
            dupes = dict()
            for date in self.dates:
                for transport in self.duplicate_transports:
                    if date in transport.dates:
                        dupes.setdefault(transport.mrsrequest, set())
                        dupes[transport.mrsrequest].add(date)

            self._duplicates_dates = collections.OrderedDict()
            for key in sorted(dupes.keys(), key=lambda x: x.creation_datetime):
                self._duplicates_dates[key] = sorted(dupes[key])

        return self._duplicates_dates

    def __str__(self):
        return str(self.display_id)

    @property
    def modes(self):
        modes = []
        for mode in ['atp', 'vp']:
            if getattr(self, f'mode{mode}', None):
                modes.append(mode)
        return modes

    def status_in(self, *names):
        return self.status in [
            getattr(self, f'STATUS_{name.upper()}')
            for name in names
        ]

    # todo: rename to status_update
    def update_status(self, user, status, log_datetime=None,
                      create_logentry=False):

        self.status = MRSRequest.get_status_id(status)
        self.status_datetime = log_datetime or timezone.now()
        self.status_user = user
        self.save()

        if not create_logentry:
            return

        self.logentries.create(
            datetime=self.status_datetime,
            user=self.status_user,
            comment=self.get_status_display(),
            action=self.status
        )

    @classmethod
    def get_status_label(cls, number):
        for flag, label in cls.STATUS_CHOICES:
            if flag == number:
                return label

    @classmethod
    def get_status_id(cls, name):
        if isinstance(name, int):
            return name
        return getattr(cls, 'STATUS_{}'.format(name.upper()))

    def denorm_reset(self):
        self.delay = self.cost = self.saving = None

    @denormalized(
        models.DecimalField,
        decimal_places=2,
        max_digits=10,
        null=True,
    )
    def taxi_cost(self):
        transport = self.transport_set.first()
        num = 1 if transport and not transport.date_return else 2
        return Decimal(
            (
                ((self.distancevp or 0) * 1.62)
                + (1.9 * num * self.transport_set.count())
            ) * 0.91
        ).quantize(TWOPLACES)

    def field_changed(self, fieldname):
        """
        If the field was changed, return its original value.
        """
        # The oldest logentry has the original value.
        if not hasattr(self, '_logentries'):
            self._logentries = self.logentries.order_by('datetime')
        for entry in self._logentries:
            if entry.data and \
               'changed' in entry.data and \
               fieldname in entry.data['changed']:
                val = entry.data['changed'][fieldname][0]
                return val

        return False

    @denormalized(
        models.DecimalField,
        decimal_places=2,
        max_digits=8,
        null=True,
        verbose_name='économie',
    )
    def saving(self):
        if not self.insured or not self.insured.shifted:
            return 0
        if not self.modevp or not self.payment_base:
            return
        return Decimal(
            float(self.taxi_cost) - float(self.payment_base)
        ).quantize(TWOPLACES)

    @denormalized(
        models.DecimalField,
        decimal_places=2,
        max_digits=5,
        null=True,
    )
    def delay(self):
        if not self.mandate_date:
            return
        mandate_datetime = datetime.datetime(
            self.mandate_date.year,
            self.mandate_date.month,
            self.mandate_date.day,
            0,
            tzinfo=pytz.timezone(settings.TIME_ZONE),
        )
        delta = mandate_datetime - self.creation_datetime_normalized
        return delta.days + (delta.seconds / 60 / 60 / 24)

    @property
    def status_days(self):
        return (timezone.now() - self.creation_datetime_normalized).days

    @property
    def days(self):
        return (timezone.now() - self.creation_datetime_normalized).days

    @property
    def creation_day_time(self):
        # french calendar date and tz time for creation_datetime
        return self.creation_datetime.astimezone(
            pytz.timezone(settings.TIME_ZONE)
        )

    @property
    def creation_day(self):
        # french calendar date for creation_datetime
        return self.creation_day_time.date()

    @property
    def waiting(self):
        return self.status not in (
            self.STATUS_VALIDATED,
            self.STATUS_REJECTED
        )

    @property
    def tooltip(self):
        if self.waiting:
            if self.days:
                return 'En attente de traitement depuis {} jours'.format(
                    self.days
                )
            else:
                return 'En attente de traitement depuis aujourd\'hui'
        return 'Traité'

    @property
    def color(self):
        if not self.waiting:
            return ''

        if self.days >= 6:
            return 'red'
        elif self.days >= 4:
            return 'orange'

        return ''

    @property
    def estimate(self):
        result = 0
        if self.distancevp:
            result += self.distancevp * 0.3
        if self.expensevp:
            result += float(self.expensevp)
        if self.expenseatp:
            result += float(self.expenseatp)
        return '%.2f' % result

    def is_allowed(self, request):
        return str(self.id) in request.session.get(self.SESSION_KEY, {})

    def allow(self, request):
        if self.SESSION_KEY not in request.session:
            request.session[self.SESSION_KEY] = {}

        request.session[self.SESSION_KEY].setdefault(str(self.id), dict())

        # The above doesn't use the request.session setter, won't automatically
        # trigger session save unless we do the following
        request.session.modified = True

    def save_bills(self):
        Bill.objects.recorded_uploads(self.id).update(mrsrequest=self)

    def delete_pmt(self):
        PMT.objects.recorded_uploads(self.id).delete()

    def save_pmt(self):
        PMT.objects.recorded_uploads(self.id).update(mrsrequest=self)

    def get_bills(self, mode=None):
        bills = getattr(self, '_bills', None)
        if not bills:
            self._bills = bills = self.bill_set.all()
        if not mode:
            return bills
        return [i for i in bills if i.mode == mode]

    # shortcuts to the above, for stupid django templates
    @property
    def billatps(self):
        return self.get_bills('atp')

    @property
    def billvps(self):
        return self.get_bills('vp')

    @property
    def total_size(self):
        if getattr(self, '_total_size', None) is None:
            self._total_size = sum(
                [
                    len(b.binary)
                    for b in
                    [*self.pmt_set.all()] + [*self.bill_set.all()]
                ]
            )
        return self._total_size

    def get_admin_url(self):
        return reverse('admin:mrsrequest_mrsrequest_change', args=[self.pk])

    def get_reject_url(self):
        return reverse('mrsrequest:reject', args=[self.pk])

    def get_validate_url(self):
        return reverse('mrsrequest:validate', args=[self.pk])

    def get_cancel_url(self):
        return reverse('demande-cancel', args=[self.pk, self.token])

    def get_update_url(self):
        return reverse('demande-update', args=[self.pk, self.token])

    @property
    def creation_date_normalized(self):
        return pytz.timezone(settings.TIME_ZONE).normalize(
            self.creation_datetime).strftime('%d/%m/%Y')

    @property
    def creation_datetime_normalized(self):
        return pytz.timezone(settings.TIME_ZONE).normalize(
            self.creation_datetime)

    @property
    def inprogress_day_number(self):
        event = self.logentries.filter(status=self.STATUS_INPROGRESS).first()

        if not event:
            return 0

        dt = pytz.timezone(settings.TIME_ZONE).normalize(event.datetime)
        return '{:03d}'.format(dt.timetuple().tm_yday)

    @property
    def order_number(self):
        previous = type(self).objects.filter(
            insured=self.insured,
            creation_datetime__gte=datetime_min(self.creation_datetime),
            creation_datetime__lte=self.creation_datetime,
        )

        if self.pk:
            previous = previous.exclude(pk=self.pk)

        number = previous.count() + 1

        if number > 99:
            return '99'

        return '{:02d}'.format(number)

    @property
    def mandate_date(self):
        dates = (self.mandate_datevp, self.mandate_dateatp)
        if dates[0] and dates[1]:
            return dates[0] if dates[0] > dates[1] else dates[1]

        for date in dates:
            if date:
                return date

    def make_display_id(self):
        normalized = pytz.timezone(settings.TIME_ZONE).normalize(
            self.creation_datetime)
        prefix = normalized.strftime('%Y%m%d')
        last = MRSRequest.objects.filter(
            display_id__startswith=prefix,
        ).order_by('display_id').last()

        number = 0
        last_display_id = getattr(last, 'display_id', None)
        if last_display_id and len(str(last_display_id)) == 12:
            number = int(str(last_display_id)[-4:]) + 1

        return int('{}{:04d}'.format(prefix, number))

    def save(self, *args, **kwargs):
        """
        Unfortunate display_id conflict handling.

        Despite our recommendations, product owner decided to generate ids
        according to rules which are victim of conflicts. At the beginning it
        was not a problem, but now that there are concurrent users on the
        platform it's of course a problem.

        Please forgive the horrible palliative fix that you are about to
        witness.
        """

        duplicate_display_id = 'duplicate key value violates unique constraint "mrsrequest_mrsrequest_display_id_key"'  # noqa

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

        if not self.display_id:
            self.display_id = self.make_display_id()

        tries = 100
        while tries:
            try:
                with transaction.atomic():
                    return super().save(*args, **kwargs)
            except IntegrityError as exc:
                # swallow duplicate "insane id" generation
                if not exc.args[0].startswith(duplicate_display_id):
                    raise

                if not tries:
                    raise

            self.display_id = self.display_id + 1
            tries -= 1
class Item(models.Model):
    """
    データ定義クラス
      各フィールドを定義する
    参考:
    ・公式 モデルフィールドリファレンス
    https://docs.djangoproject.com/ja/2.1/ref/models/fields/
    """

    # 名前
    customer_name = models.CharField(
        verbose_name='名前',
        max_length=16,
        blank=True,
        null=True,
    )

    # カナ
    customer_kana = models.CharField(
        verbose_name='カナ',
        max_length=16,
        blank=True,
        null=True,
    )

    # 郵便番号
    customer_post_code = models.IntegerField(
        verbose_name='郵便番号',
        blank=True,
        null=True,
        default=0,
        validators=[
            validators.MinValueValidator(0),
            validators.MaxValueValidator(9999999)
        ])

    # 住所
    customer_address = models.CharField(
        verbose_name='住所',
        max_length=64,
        blank=True,
        null=True,
    )

    # 電話番号
    customer_tel_number = models.CharField(
        verbose_name='電話番号',
        max_length=14,
        blank=True,
        null=True,
    )

    # メモ
    customer_memo = models.TextField(
        verbose_name='メモ',
        blank=True,
        null=True,
    )

    # タイムスタンプ
    customer_timestamp = models.DateTimeField(
        verbose_name='タイムスタンプ',
        auto_now=True  # 登録時と更新時に現在時間を設定
    )

    # 以下、管理項目

    # 作成者(ユーザー)
    created_by = models.ForeignKey(
        User,
        verbose_name='作成者',
        blank=True,
        null=True,
        related_name='CreatedBy',
        on_delete=models.SET_NULL,
        editable=False,
    )

    # 作成時間
    created_at = models.DateTimeField(
        verbose_name='作成時間',
        blank=True,
        null=True,
        editable=False,
    )

    # 更新者(ユーザー)
    updated_by = models.ForeignKey(
        User,
        verbose_name='更新者',
        blank=True,
        null=True,
        related_name='UpdatedBy',
        on_delete=models.SET_NULL,
        editable=False,
    )

    # 更新時間
    updated_at = models.DateTimeField(
        verbose_name='更新時間',
        blank=True,
        null=True,
        editable=False,
    )

    # def __str__(self):
    #     """
    #     リストボックスや管理画面での表示
    #     """
    #     return self.sample_1

    def __str__(self):
        return self.customer_name

    class Meta:
        """
        管理画面でのタイトル表示
        """
        verbose_name = verbose_name_plural = '顧客'
Exemple #6
0
class Persona(models.Model):
    identificacion = models.CharField(max_length=20, unique=True)
    tipo_documento_identificacion = models.CharField(
        max_length=30, choices=constants.TIPO_DOCUMENTO)
    nombres = models.CharField(max_length=100)
    apellidos = models.CharField(max_length=100)
    sexo = models.CharField(max_length=12, choices=constants.SEXO)
    estado_civil = models.CharField(max_length=20,
                                    choices=constants.ESTADO_CIVIL)
    edad = models.PositiveIntegerField(validators=[
        validators.MinValueValidator(1),
        validators.MaxValueValidator(100)
    ])
    telefono_1 = models.CharField(max_length=12, blank=True, null=True)
    telefono_2 = models.CharField(max_length=12, blank=True, null=True)
    telefono_3 = models.CharField(max_length=12, blank=True, null=True)
    fecha_de_registro = models.DateField(auto_now_add=True)
    fecha_ingreso = models.DateField()
    tiene_empleo = models.BooleanField(default=False)
    origen_ingreso = models.CharField(max_length=50)
    ingreso_promedio_mensual = models.PositiveIntegerField(blank=True,
                                                           null=True)
    ingreso_promedio_familiares = models.PositiveIntegerField(blank=True,
                                                              null=True)
    ingreso_promedio_mensuales = models.PositiveIntegerField(blank=True,
                                                             null=True)
    vulnerabilidad = models.BooleanField(default=False)

    hoja_de_vida = models.FileField(upload_to='cv', blank=True, null=True)
    cv_last_update = models.DateField(blank=True, null=True, editable=False)

    email = models.EmailField(blank=True, null=True)

    # educacion
    grado_escolaridad = models.ForeignKey(GradoEscolaridad)
    titulo_grado = ChainedForeignKey(TituloGrado,
                                     chained_field='grado_escolaridad',
                                     chained_model_field='grado_escolaridad')

    tipo_vivienda = models.ForeignKey(TipoVivienda)
    tipo_manzana = models.ForeignKey(TipoManzana)
    manzana = ChainedForeignKey(Manzana,
                                chained_field='tipo_manzana',
                                chained_model_field='tipo_manzana')
    casa = ChainedForeignKey(Casa,
                             chained_field='manzana',
                             chained_model_field='numero_manzana')

    def __str__(self):
        return '{} - {} : {} '.format(self.get_full_name(),
                                      self.identificacion, self.get_vivienda())

    def get_update_url(self):
        return reverse_lazy('personas:editar_persona', kwargs={'pk': self.pk})

    def get_full_name(self):
        return '{} {}'.format(self.nombres, self.apellidos)

    def get_vivienda(self):
        return self.casa

    def get_absolute_url(self):
        return reverse_lazy('personas:detalle_persona',
                            kwargs={'pk': self.ppk})

    def get_experiencias_laborales(self):
        return ExperienciaLaboralPersona.objects.filter(persona=self)

    def get_formaciones_complementarias(self):
        return FormacionComplementariaPersona.objects.filter(persona=self)

    def get_habilidades_blandas(self):
        return HabilidadBlanda.objects.filter(persona=self)

    def get_formaciones_trabajo(self):
        return FormacionTrabajoPersona.objects.filter(persona=self)

    def get_vacantes(self):
        return VacantePersona.objects.filter(persona=self)

    def get_negocios(self):
        return Negocio.objects.filter(propietario=self)

    def get_emprendimientos(self):
        return Emprendimiento.objects.filter(persona=self)
class MissionJudgeFeedback(models.Model):
    """Stores feedback from judges on a team's mission performance."""

    # The mission for which this is feedback.
    mission = models.ForeignKey(MissionConfig, on_delete=models.CASCADE)
    # The user for which this is feedback.
    user = models.ForeignKey(settings.AUTH_USER_MODEL)

    # Time spent occupying runway and airspace.
    flight_time = models.DurationField()
    # Time spent handling data on mission clock.
    post_process_time = models.DurationField()
    # Whether the team used their single timeout.
    used_timeout = models.BooleanField()

    # Whether the team had the min auto flight time.
    min_auto_flight_time = models.BooleanField()
    # The number of times the pilot took over.
    safety_pilot_takeovers = models.IntegerField(validators=[
        validators.MinValueValidator(0),
    ])
    # Number of waypoints that were captured.
    waypoints_captured = models.IntegerField(validators=[
        validators.MinValueValidator(0),
    ])
    # Number of times the UAS went out of bounds.
    out_of_bounds = models.IntegerField(validators=[
        validators.MinValueValidator(0),
    ])
    # Number of times out of bounds compromised safety.
    unsafe_out_of_bounds = models.IntegerField(validators=[
        validators.MinValueValidator(0),
    ])
    # Whether something fell off UAS during flight.
    things_fell_off_uas = models.BooleanField()
    # Whether the UAS crashed.
    crashed = models.BooleanField()

    # Accuracy of drop in feet.
    air_drop_accuracy = models.IntegerField(
        choices=pb_utils.FieldChoicesFromEnum(
            interop_admin_api_pb2.MissionJudgeFeedback.AirDropAccuracy))
    # Whether the UGV drove to the specified location.
    ugv_drove_to_location = models.BooleanField()

    # Grade of team performance [0, 100].
    operational_excellence_percent = models.FloatField(validators=[
        validators.MinValueValidator(0),
        validators.MaxValueValidator(100),
    ])

    class Meta:
        unique_together = (('mission', 'user'), )

    def proto(self):
        """Get the proto formatted feedback."""
        feedback = interop_admin_api_pb2.MissionJudgeFeedback()

        feedback.flight_time_sec = self.flight_time.total_seconds()
        feedback.post_process_time_sec = self.post_process_time.total_seconds()
        feedback.used_timeout = self.used_timeout

        feedback.min_auto_flight_time = self.min_auto_flight_time
        feedback.safety_pilot_takeovers = self.safety_pilot_takeovers
        feedback.waypoints_captured = self.waypoints_captured
        feedback.out_of_bounds = self.out_of_bounds
        feedback.unsafe_out_of_bounds = self.unsafe_out_of_bounds
        feedback.things_fell_off_uas = self.things_fell_off_uas
        feedback.crashed = self.crashed

        feedback.air_drop_accuracy = self.air_drop_accuracy
        feedback.ugv_drove_to_location = self.ugv_drove_to_location

        feedback.operational_excellence_percent = self.operational_excellence_percent

        return feedback
Exemple #8
0
class EducationalModule(models.Model):
    STATUSES = (
        (HIDDEN, _('Скрыт')),
        (DIRECT, _('Доступ по ссылке')),
        (PUBLISHED, _('Опубликован')),
    )
    code = models.SlugField(verbose_name=_('Код'), unique=True)
    title = models.CharField(verbose_name=_('Название'), max_length=200)
    status = models.CharField(_('Статус'),
                              max_length=16,
                              choices=STATUSES,
                              default='hidden')
    courses = SortedManyToManyField(Course,
                                    verbose_name=_('Курсы'),
                                    related_name='education_modules')
    cover = models.ImageField(
        _('Обложка EM'),
        upload_to='edmodule_cover',
        blank=True,
        help_text=
        _('Минимум {0}*{1}, картинки большего размера будут сжаты до этого размера'
          ).format(*getattr(settings, 'EDMODULE_COVER_IMAGE_SIZE',
                            DEFAULT_COVER_SIZE)))
    about = models.TextField(verbose_name=_('Описание'), blank=False)
    price = models.IntegerField(verbose_name=_('Стоимость'),
                                blank=True,
                                null=True)
    discount = models.IntegerField(verbose_name=_('Скидка'),
                                   blank=True,
                                   default=0,
                                   validators=[
                                       validators.MinValueValidator(0),
                                       validators.MaxValueValidator(100)
                                   ])
    vacancies = models.TextField(verbose_name=_('Вакансии'),
                                 blank=True,
                                 default='',
                                 help_text=_('HTML блок'))
    subtitle = models.TextField(
        verbose_name=_('Подзаголовок'),
        blank=True,
        default='',
        help_text=_('от 1 до 3 элементов, каждый с новой строки'))
    sum_ratings = models.PositiveIntegerField(verbose_name=_('Сумма оценок'),
                                              default=0)
    count_ratings = models.PositiveIntegerField(
        verbose_name=_('Количество оценок'), default=0)

    class Meta:
        verbose_name = _('Образовательный модуль')
        verbose_name_plural = _('Образовательные модули')

    def __str__(self):
        return '%s - %s' % (self.code, ', '.join(
            self.courses.values_list('slug', flat=True)))

    @cached_property
    def duration(self):
        """
        сумма длительностей курсов (в неделях)
        """
        duration = 0
        for c, s in self.courses_with_closest_sessions:
            d = s.get_duration() if s else c.duration
            if not d:
                return 0
            duration += d
        return duration

    @cached_property
    def whole_work(self):
        work = 0
        for c, s in self.courses_with_closest_sessions:
            if s:
                w = (s.get_duration() or 0) * (s.get_workload() or 0)
            else:
                w = (c.duration or 0) * (c.workload or 0)
            if not w:
                return 0
            work += w
        return work

    @property
    def workload(self):
        work = self.whole_work
        duration = self.duration
        if self.duration:
            return int(round(float(work) / duration, 0))
        return 0

    @property
    def instructors(self):
        """
        объединение множества преподавателей всех курсов модуля
        упорядочивание по частоте вхождения в сессии, на которые мы записываем пользователя
        """
        d = {}
        for c in self.courses.all():
            if c.next_session:
                for i in c.next_session.get_instructors():
                    d[i] = d.get(i, 0) + 1
            else:
                for i in c.instructor.all():
                    d[i] = d.get(i, 0) + 1
        result = sorted(list(d.items()), key=lambda x: x[1], reverse=True)
        return [i[0] for i in result]

    @property
    def categories(self):
        return self._get_sorted('categories')

    def get_authors(self):
        return self._get_sorted('authors')

    def get_partners(self):
        return self._get_sorted('partners')

    def get_authors_and_partners(self):
        result = []
        for i in self.get_authors() + self.get_partners():
            if not i in result:
                result.append(i)
        return result

    def _get_sorted(self, attr):
        """
        Возвращает список элементов attr отсортированный по количеству курсов,
        в которых этот attr встречается. Используется, например, для списка категорий
        модуля, которые отстортированы по количеству курсов, в которых они встречаются
        """
        d = {}
        for c in self.courses_extended.prefetch_related(attr):
            for item in getattr(c, attr).all():
                d[item] = d.get(item, 0) + 1
        result = sorted(list(d.items()), key=lambda x: x[1], reverse=True)
        return [i[0] for i in result]

    def get_schedule(self):
        """
        список тем
        """
        schedule = []
        all_courses = self.courses.values_list('id', flat=True)
        for c in self.courses_extended.prefetch_related('course'):
            if c.course.id not in all_courses:
                schedule.append({
                    'course': {
                        'title': c.course.title
                    },
                    'schedule': ''
                })
            else:
                schedule.append({
                    'course': {
                        'title': c.course.title
                    },
                    'schedule': c.themes
                })
        return schedule

    def get_rating(self):
        if self.count_ratings:
            return round(float(self.sum_ratings) / self.count_ratings, 2)
        return 0

    def get_related(self):
        """
        получение похожих курсов и специализаций (от 0 до 2)
        """
        categories = self.categories
        if not categories:
            return []
        modules = EducationalModule.objects.exclude(id=self.id).filter(
            courses__extended_params__categories__in=categories,
            status='published').distinct()
        courses = EdmoduleCourse.objects.exclude(
            id__in=self.courses.values_list('id', flat=True)).filter(
                extended_params__categories__in=categories,
                status='published').distinct()
        related = []
        if modules:
            related.append({
                'type': 'em',
                'item': random.sample(list(modules), 1)[0]
            })
        if courses:
            sample = random.sample(list(courses), min(len(courses), 2))
            for i in range(2 - len(related)):
                try:
                    related.append({'type': 'course', 'item': sample[i]})
                except IndexError:
                    pass
        return related

    def get_sessions(self):
        """
        хелпер для выбора сессий
        """
        return [i.next_session for i in self.courses.all()]

    @cached_property
    def courses_extended(self):
        """
        CourseExtendedParameters всех курсов модуля
        """
        return CourseExtendedParameters.objects.filter(
            course__id__in=self.courses.values_list('id', flat=True))

    def get_module_profit(self):
        """ для блока "что я получу в итоге" """
        data = []
        for c in self.courses_extended:
            if c.profit:
                data.extend(c.profit.splitlines())
        data = [i.strip() for i in data if i.strip()]
        return list(set(data))

    def get_requirements(self):
        try:
            s = self.extended_params.requirements or ''
            return [i.strip() for i in s.splitlines() if i.strip()]
        except:
            pass

    def get_price_list(self, for_user=None):
        """
        :return: {
            'courses': [(курс(Course), цена(int), ...],
            'price': цена без скидок (int),
            'whole_price': цена со скидкой (float),
            'discount': скидка (int)
        }
        """
        courses = self.courses.all()
        # берем цену ближайшей сессии, на которую можно записаться, или предыдущей
        session_for_course = {}
        now = timezone.now()
        course_paid = []
        if for_user and for_user.is_authenticated:
            # если пользователь платил за какую-то сессию курса и успешно ее окончил или она
            # еще не завершилась, цена курса для него 0
            reasons = EnrollmentReason.objects.filter(
                participant__user=for_user,
                session_enrollment_type__mode='verified').select_related(
                    'participant', 'participant__session')
            payment_for_course = defaultdict(list)
            for r in reasons:
                payment_for_course[r.participant.session.course_id].append(r)
            for course_id, payments in payment_for_course.items():
                should_pay = True
                for r in payments:
                    if r.participant.is_graduate:
                        should_pay = False
                        break
                    if r.participant.session.datetime_ends and r.participant.session.datetime_ends > now:
                        should_pay = False
                        break
                if not should_pay:
                    course_paid.append(course_id)
        exclude = {'id__in': course_paid}
        sessions = CourseSession.objects.filter(
            course__in=courses.exclude(**exclude),
            datetime_end_enroll__isnull=False,
            datetime_start_enroll__lt=now).exclude(
                **exclude).order_by('-datetime_end_enroll')
        courses_with_sessions = defaultdict(list)
        for s in sessions:
            courses_with_sessions[s.course_id].append(s)
        for c, course_sessions in courses_with_sessions.items():
            if course_sessions:
                session_for_course[c] = course_sessions[0]
        types = dict([
            (i.session.id, i.price)
            for i in SessionEnrollmentType.objects.filter(
                session__in=list(session_for_course.values()), mode='verified')
        ])
        result = {'courses': []}
        for c in courses:
            s = session_for_course.get(c.id)
            if s:
                result['courses'].append((c, types.get(s.id, 0)))
            else:
                result['courses'].append((c, 0))
        price = sum([i[1] for i in result['courses']])
        whole_price = price * (1 - self.discount / 100.)
        result.update({
            'price': price,
            'whole_price': whole_price,
            'discount': self.discount
        })
        return result

    def get_start_date(self):
        """
        дата старта первого курса модуля
        """
        c = self.courses.first()
        if c and c.next_session:
            return c.next_session.datetime_starts

    def course_status_params(self):
        from .utils import get_status_dict
        c = self.get_closest_course_with_session()
        if c:
            return get_status_dict(c[1])
        return {}

    @property
    def count_courses(self):
        return self.courses.count()

    @cached_property
    def courses_with_closest_sessions(self):
        from .utils import choose_closest_session
        courses = self.courses.exclude(extended_params__is_project=True)
        return [(c, choose_closest_session(c)) for c in courses]

    def get_closest_course_with_session(self):
        """
        первый курс, не являющийся проектом, и соответствующая сессия модуля
        """
        for c in self.courses.filter(extended_params__is_project=False):
            session = c.next_session
            if session and session.get_verified_mode_enrollment_type():
                return c, session

    def may_enroll(self):
        """
        Проверка того, что пользователь может записаться на модуль
        :return: bool
        """
        courses = self.courses_with_closest_sessions
        return all(i[1] and i[1].allow_enrollments() for i in courses)

    def may_enroll_on_project(self, user):
        """
        Проверка того, что пользователь может записаться на проект
        :param user: User
        :return: bool
        """
        if not user.is_authenticated:
            return False
        if not EducationalModuleEnrollment.objects.filter(
                user=user, module=self, is_active=True).exists():
            return False
        courses = self.courses.filter(
            extended_params__is_project=False).values_list('id', flat=True)
        passed = {i: False for i in courses}
        participants = Participant.objects.filter(
            session__course__id__in=courses,
            user=user).values_list('session__course__id', 'is_graduate')
        for course_id, is_graduate in participants:
            if is_graduate:
                passed[course_id] = True
        return all(i for i in list(passed.values()))

    def get_available_enrollment_types(self,
                                       mode=None,
                                       exclude_expired=True,
                                       active=True):
        """ Возвращает доступные варианты EducationalModuleEnrollmentType для текущего модуля """
        qs = EducationalModuleEnrollmentType.objects.filter(module=self)
        if active:
            qs = qs.filter(active=True)
        if mode:
            qs = qs.filter(mode=mode)
        if exclude_expired and mode == 'verified':
            qs = qs.exclude(buy_expiration__lt=timezone.now()).filter(
                models.Q(buy_start__isnull=True)
                | models.Q(buy_start__lt=timezone.now()))
        return qs

    def get_verified_mode_enrollment_type(self):
        """
        Метод аналогичный CourseSession
        """
        return self.get_available_enrollment_types(mode='verified').first()

    def get_enrollment_reason_for_user(self, user):
        """
        queryset EducationalModuleEnrollmentReason для пользователя, первый элемент - полностью оплаченный,
        если такой есть
        """
        if user.is_authenticated:
            return EducationalModuleEnrollmentReason.objects.filter(
                enrollment__user=user,
                enrollment__module=self,
            ).order_by('-full_paid').first()

    def get_first_session_to_buy(self, user):
        """
        Сессия первого курса, который пользователь может купить.
        Возвращает (сессия, цена) или None
        """
        auth = user.is_authenticated if user else None
        for course in self.courses.exclude(extended_params__is_project=True):
            session = course.next_session
            if session:
                enr_type = session.get_verified_mode_enrollment_type()
                if enr_type and auth:
                    if not enr_type.is_user_enrolled(user):
                        return session, enr_type.price
                elif enr_type:
                    return session, enr_type.price
"""Aerial position model."""

import logging
from auvsi_suas.models import distance
from auvsi_suas.models.gps_position import GpsPosition
from django.contrib import admin
from django.core import validators
from django.db import models

logger = logging.getLogger(__name__)

ALTITUDE_MSL_FT_MIN = -2000  # Lowest point on earth with buffer.
ALTITUDE_MSL_FT_MAX = 396000  # Edge of atmosphere.
ALTITUDE_VALIDATORS = [
    validators.MinValueValidator(ALTITUDE_MSL_FT_MIN),
    validators.MaxValueValidator(ALTITUDE_MSL_FT_MAX),
]


class AerialPosition(models.Model):
    """Aerial position which consists of a GPS position and an altitude."""
    # GPS position.
    gps_position = models.ForeignKey(GpsPosition, on_delete=models.CASCADE)

    # Altitude (MSL) in feet.
    altitude_msl = models.FloatField(validators=ALTITUDE_VALIDATORS)

    def distance_to(self, other):
        """Computes distance to another position.

        Args:
 def __init__(self, *args, **kwargs):
     kwargs['validators'] = (validators.MinValueValidator(0.0),
                             validators.MaxValueValidator(1.0))
     super().__init__(*args, **kwargs)
Exemple #11
0
class Transaction(TimestampModel):
    uuid = models.UUIDField(default=uuid.uuid4, primary_key=True)
    type = models.CharField(
        _("Type"),
        max_length=254,
        default=constants.EXPENDITURE,
        choices=constants.TRANSACTION_TYPE_CHOICES,
    )
    amount = models.DecimalField(
        _("Amount"),
        max_digits=12,
        decimal_places=2,
        validators=[validators.MinValueValidator(0)],
    )
    author = models.ForeignKey(UserModel,
                               related_name="transactions",
                               on_delete=models.CASCADE)
    category = models.ForeignKey(
        Category,
        related_name="transactions",
        on_delete=models.SET_NULL,
        verbose_name=_("Category"),
        blank=True,
        null=True,
    )
    tags = models.ManyToManyField(Tag,
                                  related_name="transactions",
                                  verbose_name=_("Tags"))
    account = models.ForeignKey(
        "accounts.Account",
        related_name="transactions",
        on_delete=models.CASCADE,
        verbose_name=_("Account"),
    )
    expenditure_counterpart = models.OneToOneField(
        "self",
        related_name="income_counterpart",
        on_delete=models.CASCADE,
        verbose_name=_("Expenditure counterpart"),
        blank=True,
        null=True,
    )
    description = models.CharField(_("Description"),
                                   max_length=1000,
                                   blank=True)

    objects = managers.TransactionManager()

    class Meta:
        ordering = ["-created_at"]
        verbose_name = _("Transaction")
        verbose_name_plural = _("Transactions")

    def __str__(self):
        return f"{self.get_type_display()} - {self.amount}"

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

    def save(self, *args, **kwargs):
        expenditure_condition = (self.expenditure_counterpart
                                 and self.type == constants.EXPENDITURE)

        try:
            income_condition = self.income_counterpart and self.type == constants.INCOME
        except Transaction.DoesNotExist:
            income_condition = False

        if expenditure_condition or income_condition:
            raise ValueError(
                _("Cannot change the type of a transaction that is part of a transfer"
                  ))

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

    @property
    def is_income(self):
        return self.type == constants.INCOME

    @property
    def is_expenditure(self):
        return self.type == constants.EXPENDITURE

    @property
    def type_icon_class(self):
        if self.is_expenditure:
            return "text-danger fa fa-arrow-circle-down"
        else:
            return "text-success fa fa-arrow-circle-up"

    @property
    def currency(self):
        return self.account.currency

    @property
    def is_part_of_transfer(self):
        try:
            return self.expenditure_counterpart or self.income_counterpart
        except Transaction.DoesNotExist:
            return False

    @property
    def exchange_rate(self):
        if not self.is_part_of_transfer:
            return

        divisible = self.amount if self.is_income else self.income_counterpart.amount

        divisor = self.expenditure_counterpart.amount if self.is_income else self.amount

        return divisible / divisor
Exemple #12
0
class Person(models.Model):
    first_name = models.CharField(
        max_length=70,
        verbose_name='Prénom',
        validators=name_validators,
    )
    last_name = models.CharField(
        max_length=70,
        verbose_name='Nom de famille',
        validators=name_validators,
    )
    birth_date = models.DateField(
        null=True,
        verbose_name='Date de naissance',
        validators=[
            validators.MinValueValidator(
                datetime.date(year=1900, month=1, day=1))
        ],
    )
    email = models.EmailField(
        null=True,
        verbose_name='Email',
    )
    use_email = models.BooleanField(
        default=False,
        null=True,
        blank=True,
        verbose_name="L'assuré autorise à utiliser son email.",
    )
    nir = models.CharField(
        max_length=13,
        verbose_name='Numéro de sécurité sociale',
        validators=[
            nir_validate_alphanumeric,
            validators.MinLengthValidator(13),
            # note that max_length attribute implies a max length validator
        ])
    shifted = models.NullBooleanField(
        default=None,
        null=True,
        blank=True,
        verbose_name='Assuré a basculé',
    )
    creation_datetime = models.DateTimeField(
        default=timezone.now,
        db_index=True,
        verbose_name='Date et heure de création',
    )

    class Meta:
        ordering = (
            'last_name',
            'first_name',
        )
        verbose_name = 'Personne'

    def __str__(self):
        return '%s %s %s' % (self.first_name, self.last_name, self.birth_date)

    def save(self, *args, **kwargs):
        """
        Validate all fields, since validators are not run on save by default.
        """
        self.full_clean()
        return super(Person, self).save(*args, **kwargs)

    def get_dates(self):
        dates = {'depart': dict(), 'return': dict()}

        valids = self.mrsrequest_set.filter(
            status=self.mrsrequest_set.model.STATUS_VALIDATED
        ).prefetch_related('transport_set')
        for mrsrequest in valids:
            transports = mrsrequest.transport_set.exclude(date_depart=None)
            for transport in transports:
                for i in ('depart', 'return'):
                    value = getattr(transport, f'date_{i}')

                    if i == 'return' and not value:
                        continue

                    if value not in dates[i].keys():
                        dates[i][value] = []
                    dates[i][value].append(mrsrequest)

        return dates

    def get_duplicate_dates(self):
        dupes = {'depart': dict(), 'return': dict()}

        for i, dates in self.get_dates().items():
            for date, mrsrequests in dates.items():
                if len(mrsrequests) == 1:
                    continue

                dupes[i][date] = mrsrequests

        return {k: v for k, v in dupes.items() if v}
Exemple #13
0
class Item(models.Model):
    TYPE_CHOICES = (
        (1, 'TVアニメ'),
        (2, '劇場アニメ'),
    )
    MEDIA_CHOICES = ((1, 'BD'), (2, 'DVD'), (3, 'BD/DVD両方'))

    title = models.CharField(
        verbose_name='タイトル',
        max_length=250,
    )
    place = models.CharField(
        verbose_name='保管場所',
        max_length=250,
    )
    type = models.IntegerField(
        verbose_name='TV/映画',
        choices=TYPE_CHOICES,
        default=1,
    )
    media = models.IntegerField(
        verbose_name='メディア形式',
        choices=MEDIA_CHOICES,
        default=1,
    )
    year = models.IntegerField(
        verbose_name='制作年',
        validators=[validators.MinValueValidator(1963)],
        blank=True,
        null=True,
    )
    epsode_number = models.IntegerField(
        verbose_name='話数',
        validators=[validators.MinValueValidator(1)],
        blank=True,
        null=True,
    )
    media_number = models.IntegerField(
        verbose_name='巻数',
        validators=[validators.MinValueValidator(1)],
        blank=True,
        null=True,
    )
    production = models.CharField(
        verbose_name='制作会社',
        max_length=250,
        blank=True,
        null=True,
    )
    voiceactor = models.CharField(
        verbose_name='主演声優',
        max_length=250,
        blank=True,
        null=True,
    )
    director = models.CharField(
        verbose_name='監督',
        max_length=200,
        blank=True,
        null=True,
    )
    memo = models.TextField(
        verbose_name='備考',
        max_length=300,
        blank=True,
        null=True,
    )
    created_at = models.DateTimeField(
        verbose_name='登録日',
        auto_now_add=True,
        blank=True,
        null=True,
    )

    # 以下は管理サイト上の表示設定
    def _str_(self):
        return self.name

    class Meta:
        verbose_name = 'アイテム'
        verbose_name_plural = 'アイテム'
class Cache(models.Model):
    defined_storage_name = models.CharField(
        db_index=True, help_text=_(
            'Internal name of the defined storage for this cache.'
        ), max_length=96, unique=True, verbose_name=_('Defined storage name')
    )
    maximum_size = models.BigIntegerField(
        help_text=_('Maximum size of the cache in bytes.'), validators=[
            validators.MinValueValidator(limit_value=1)
        ], verbose_name=_('Maximum size')
    )

    class Meta:
        verbose_name = _('Cache')
        verbose_name_plural = _('Caches')

    def __str__(self):
        return force_text(self.label)

    def get_files(self):
        return CachePartitionFile.objects.filter(partition__cache__id=self.pk)

    def get_maximum_size_display(self):
        return filesizeformat(bytes_=self.maximum_size)

    get_maximum_size_display.help_text = _(
        'Size at which the cache will start deleting old entries.'
    )
    get_maximum_size_display.short_description = _('Maximum size')

    def get_defined_storage(self):
        return DefinedStorage.get(name=self.defined_storage_name)

    def get_total_size(self):
        """
        Return the actual usage of the cache.
        """
        return self.get_files().aggregate(
            file_size__sum=Sum('file_size')
        )['file_size__sum'] or 0

    def get_total_size_display(self):
        return format_lazy(
            '{} ({:0.1f}%)',
            filesizeformat(bytes_=self.get_total_size()),
            self.get_total_size() / self.maximum_size * 100
        )

    get_total_size_display.short_description = _('Current size')
    get_total_size_display.help_text = _('Current size of the cache.')

    @cached_property
    def label(self):
        return self.get_defined_storage().label

    def prune(self):
        """
        Deletes files until the total size of the cache is below the allowed
        maximum size of the cache.
        """
        while self.get_total_size() > self.maximum_size:
            self.get_files().earliest().delete()

    def purge(self, _user=None):
        """
        Deletes the entire cache.
        """
        for partition in self.partitions.all():
            partition.purge()

        event_cache_purged.commit(actor=_user, target=self)

    def save(self, *args, **kwargs):
        _user = kwargs.pop('_user', None)
        with transaction.atomic():
            is_new = not self.pk
            result = super(Cache, self).save(*args, **kwargs)
            if is_new:
                event_cache_created.commit(
                    actor=_user, target=self
                )
            else:
                event_cache_edited.commit(
                    actor=_user, target=self
                )

        self.prune()
        return result

    @cached_property
    def storage(self):
        return self.get_defined_storage().get_storage_instance()
Exemple #15
0
class ActivityPerformance(Performance):
    # Uses basic Performance to bind a duration & description to a contract
    """Activity performance model."""

    contract = models.ForeignKey(Contract, on_delete=models.PROTECT)
    performance_type = models.ForeignKey(PerformanceType,
                                         on_delete=models.PROTECT)
    contract_role = models.ForeignKey(ContractRole,
                                      null=True,
                                      on_delete=models.PROTECT)
    description = models.TextField(max_length=255, blank=True, null=True)
    duration = models.DecimalField(max_digits=4,
                                   decimal_places=2,
                                   default=1.00,
                                   validators=[
                                       validators.MinValueValidator(
                                           Decimal('0.01')),
                                       validators.MaxValueValidator(24),
                                   ])

    def __str__(self):
        """Return a string representation."""
        return '%s - %s' % (self.performance_type, super().__str__())

    @classmethod
    def perform_additional_validation(cls, data, instance=None):
        """Perform additional validation on the object."""
        super().perform_additional_validation(data, instance=instance)

        instance_id = instance.id if instance else None  # noqa

        contract = data.get('contract', getattr(instance, 'contract', None))
        performance_type = data.get(
            'performance_type', getattr(instance, 'performance_type', None))
        contract_role = data.get('contract_role',
                                 getattr(instance, 'contract_role', None))

        if contract and contract_role:
            # Ensure that contract is a project contract
            if not isinstance(contract, ProjectContract):
                raise ValidationError(
                    _('The selected contract role is not valid for the selected contract.'
                      ), )
            # Ensure that contract role is valid for contract
            performance = data.get('performance',
                                   getattr(instance, 'performance', None))
            if performance:
                timesheet = Timesheet.objects.get(id=performance.timesheet)
                user_id = timesheet.user if timesheet else None  # noga
                contract_user = ContractUser.objects.filter(
                    user=user_id,
                    contract=contract.id,
                    contract_role=contract_role)
                if not contract_user:
                    raise ValidationError(
                        _('The selected contract role is not valid for the current user.'
                          ), )

        if contract and performance_type:
            # Ensure the performance type is valid for the contract
            allowed_types = list(contract.performance_types.all())

            if allowed_types and (performance_type not in allowed_types):
                raise ValidationError(
                    _('The selected performance type is not valid for the selected contract'
                      ), )

    def get_validation_args(self):
        """Get a dict used for validation based on this instance."""
        return merge_dicts(
            super().get_validation_args(), {
                'contract': getattr(self, 'contract', None),
                'performance_type': getattr(self, 'performance_type', None),
            })
Exemple #16
0
class Ticket(models.Model):
    """ 販売されているチケット
    """
    STATUS_DISPLAY = 0
    STATUS_STOPPED = 1
    STATUS_SOLD_OUT = 2

    STATUS_CHOICES = (
        (STATUS_DISPLAY, '出品中'),  # 現在出品されている。購入可能な状態
        (STATUS_STOPPED, '出品停止'),  # 以降購入ができない状態。購入済みのチケットは有効
        (STATUS_SOLD_OUT, '完売')  # 出品したチケットが売切れた状態
    )

    seller = models.ForeignKey('tbpauth.User',
                               on_delete=models.CASCADE,
                               related_name='selling_tickets')
    name = models.CharField("チケット名", max_length=128)
    category = models.ForeignKey(Category,
                                 verbose_name="カテゴリー",
                                 on_delete=models.SET_NULL,
                                 null=True,
                                 blank=True,
                                 related_name='tickets')
    start_date = models.DateField("開催日")
    price = models.PositiveIntegerField("金額(円)",
                                        validators=[
                                            validators.MinValueValidator(100),
                                            StepValueValidator(100)
                                        ])
    quantity = models.PositiveIntegerField(
        "販売枚数(枚)", validators=[validators.MinValueValidator(1)])

    status = models.PositiveIntegerField("販売ステータス",
                                         choices=STATUS_CHOICES,
                                         default=STATUS_DISPLAY)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'ticket'
        verbose_name = 'チケット'
        verbose_name_plural = 'チケット'

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('tickets:detail', kwargs={'ticket_id': self.id})

    def status_is_display(self):
        """ ステータスが「出品中」の場合にTrueを返す
        """
        return self.status == self.STATUS_DISPLAY

    def fee_rate(self):
        """ 手数料の割合を小数で返す

        販売枚数とカテゴリーの追加手数料から計算して小数で返す
        """
        if self.quantity < 50:
            fee_rate = 0.05
        elif self.quantity < 100:
            fee_rate = 0.03
        else:
            fee_rate = 0.01

        if self.category:
            fee_rate += self.category.extra_fee_rate

        return fee_rate

    def fee(self):
        """ 手数料の金額を返す
        """
        return int(round(self.fee_rate() * self.price))

    def stock_amount(self):
        """ チケットの残り在庫数を返す
        販売枚数から購入履歴 Purchase モデルの合計枚数をマイナスして計算する。
        """
        agg = self.purchases.aggregate(sum_amount=models.Sum('amount'))
        return self.quantity - (agg['sum_amount'] or 0)

    # 以下、表示用の関数。値に単位などを追加して文字列として返す

    def price_display(self):
        return '{:,d}円'.format(self.price)

    def quantity_display(self):
        return '{:,d}枚'.format(self.quantity)

    def fee_rate_display(self):
        return '{:0.0f}%'.format(self.fee_rate() * 100)

    def fee_display(self):
        return '{:,d}円 / 枚'.format(self.fee())

    def stock_amount_display(self):
        return '{:,d}枚'.format(self.stock_amount())
Exemple #17
0
class Application(models.Model):
    organization = models.ForeignKey(Organization, unique_for_year='year')
    year = afap.modelfields.YearField()
    president = models.ForeignKey(Person, related_name='president', null=True, blank=True)
    email_president = models.BooleanField(default=True)
    treasurer = models.ForeignKey(Person, related_name='treasurer', null=True, blank=True)
    email_treasurer = models.BooleanField(default=True)
    advisor = models.ForeignKey(Person, related_name='advisor', null=True, blank=True)
    email_advisor = models.BooleanField(default=True)
    members = models.IntegerField("number of members", default=0,
            validators=[validators.MinValueValidator(0)])
    dues = models.DecimalField(default='0', max_digits=DECIMAL_DIGITS,
            decimal_places=DECIMAL_PLACES)
    new_members = models.IntegerField("anticipated new members", default=0)
    purpose = models.TextField(blank=True)
    membership_requirements = models.TextField(blank=True)
    note = models.TextField("notes", blank=True)
    last_modified = models.DateTimeField(blank=True)
    provisional = models.BooleanField(default=False)
    balance_forward = models.DecimalField("Balance forward", max_digits=DECIMAL_DIGITS,
            decimal_places=DECIMAL_PLACES, default=0)
    afap_income = models.DecimalField("AFAP Income", max_digits=DECIMAL_DIGITS,
            decimal_places=DECIMAL_PLACES, default=0,
            validators=[validators.MinValueValidator(0)])

    def __unicode__(self):
        return u'Application for %s (%d)' % (self.organization.name,
                self.year.year,)

    def _year_for_admin(self):
        return self.year.year
    _year_for_admin.admin_order_field = 'year'
    _year_for_admin.short_description = 'year'

    def has_org_info(self):
        return (self.members > 0 or self.dues > 0 or self.new_members != 0
            or self.purpose != '' or self.membership_requirements != '')

    def has_budget_request(self):
        return (self.lineitem_set.exists() or self.balance_forward != 0 or
            self.afap_income != 0)

    def approved(self):
        for approval in self.approval_set.all():
            if not approval.approved:
                return False
        return True

    def request_amount(self):
        sums = []
        for category in LineItemCategory.objects.filter(years__date=self.year):
            sums.append(sum_line_items(
                LineItem.objects.filter(application=self, category=category)))
        total_sum = sum(sums) - self.balance_forward - self.afap_income
        total_sum = total_sum or 0
        return total_sum
        
    # look into only updating last_modified when the user makes a chance,
    # not when an admin does
    def save(self, *args, **kwargs):
        '''On save, update last_modified'''
        self.last_modified = datetime.datetime.now()
        approvers = (self.president, self.treasurer, self.advisor)
        for approval in self.approval_set.all():
            if approval.approver not in approvers:
                approval.delete()
            else:
                approval.approved = False
                approval.approved_at = None
                approval.generate_key()
                approval.save()
        for person in [person for person
                in (self.president, self.treasurer, self.advisor)
                if person is not None]:
            try:
                self.approval_set.get(approver=person)
            except (Approval.DoesNotExist):
                Approval(application=self, approver=person).save()
        return super(Application, self).save(*args, **kwargs)

    def print_to_pdf(self, pdf):
        return

    def organization_constituency(self):
        return self.organization.constituency_group
    organization_constituency.description = "the applying organization's const"

    def email(self, subject, message, from_, tuple_=False):
        recps = []
        if self.president and self.email_president:
            recps.append(self.president.detailed_address())
        if self.treasurer and self.email_treasurer:
            recps.append(self.treasurer.detailed_address())
        if self.advisor and self.email_advisor:
            recps.append(self.advisor.detailed_address())
        if tuple_:
            return (subject, message, from_, recps)
        else:
            mail.send_mail(subject, message, from_, recps, fail_silently=False)
        return
Exemple #18
0
CONSTANCE_SUPERUSER_ONLY = False
CONSTANCE_ADDITIONAL_FIELDS = {
    'TIMEZONE': ('django.forms.fields.ChoiceField', {
        'widget': 'django.forms.Select',
        'choices': [(name, name) for name in pytz.common_timezones],
    }),
    'WAIT_INTERVAL_MINUTES': ('django.forms.fields.IntegerField', {
        'widget':
        'django.forms.TextInput',
        'widget_kwargs': {
            'attrs': {
                'size': 10
            }
        },
        'validators':
        [validators.MinValueValidator(0),
         validators.MaxValueValidator(600)],
    }),
    'FADE_ASSETS_MS': ('django.forms.fields.IntegerField', {
        'widget':
        'django.forms.TextInput',
        'widget_kwargs': {
            'attrs': {
                'size': 10
            }
        },
        'validators':
        [validators.MinValueValidator(0),
         validators.MaxValueValidator(10000)],
    }),
}
Exemple #19
0
class MRSRequestCreateForm(forms.ModelForm):
    # do not trust this field, it's used for javascript and checked
    # by the view for permission against the request session, but is
    # NOT to be trusted as input: don't use data['mrsrequest_uuid'] nor
    # cleaned_data['mrsrequest_uuid'], you've been warned.
    mrsrequest_uuid = forms.CharField(widget=forms.HiddenInput, required=False)

    pmt = MRSAttachmentField(
        PMT,
        'mrsrequest:pmt_upload',
        'mrsrequest:pmt_download',
        20,
        label='Prescription Médicale de Transport obligatoire',
        help_text=PMT_HELP,
        required=False,
    )

    pmt_pel = forms.ChoiceField(
        choices=(
            ('pmt', 'Prescription papier (PMT)'),
            ('pel', 'Prescription électronique (PMET)'),
            ('convocation', 'Convocation Service Médical'),
        ),
        initial='pmt',
        label='',
        widget=forms.RadioSelect,
    )

    pel = forms.CharField(
        label='Numéro de Prescription Électronique',
        help_text=PEL_HELP,
        required=False,
    )

    convocation = DateFieldNative(
        label=(
            'Date du rendez-vous avec le médecin'
            ' conseil de l\'Assurance Maladie'
        ),
        help_text='Au format jj/mm/aaaa, par exemple: 31/12/2000',
        required=False,
    )

    billvps = MRSAttachmentField(
        BillVP,
        'mrsrequest:billvp_upload',
        'mrsrequest:bill_download',
        20,
        label='Justificatifs',
        required=False,
        help_text=(
            'Joindre vos justificatifs de péage'
            ' <span data-parking-enable>'
            ' / stationnement.'
            ' </span><br>'
            'Format <b>jpeg</b>, <b>png</b> ou <b>pdf</b> -'
            '<b>4Mo maximum</b> par fichier.'
        )
    )

    billatps = MRSAttachmentField(
        BillATP,
        'mrsrequest:billatp_upload',
        'mrsrequest:bill_download',
        20,
        label='Justificatifs',
        required=False,
        help_text=(
            'Joindre vos justificatifs de transport en commun.<br>'
            'Format <b>jpeg</b>, <b>png</b> ou <b>pdf</b> -'
            '<b>4Mo maximum</b> par fichier.'
        )
    )

    caisse = ActiveCaisseChoiceField(
        otherchoice=True,
        label='',
        help_text='Votre caisse n\'apparaît pas dans la liste ? Elle n\'a pas '
                  'encore rejoint le dispositif MRS. Cliquez sur "Autre" pour '
                  'la sélectionner et recevoir un e-mail dès que celle-ci '
                  'sera disponible !'
    )

    region = ActiveRegionChoiceField(
        label='',
    )

    distancevp = CharFieldNative(
        label='Nombre total de kilomètres',
        help_text=' ',
    )

    expenseatp = AllowedCommaDecimalField(
        decimal_places=2,
        max_digits=6,
        validators=[validators.MinValueValidator(Decimal('0.00'))],
        label='Frais de transports',
        help_text=(
            'Somme totale des frais de transport en commun (en € TTC)'
        ),
        required=False,
        widget=forms.TextInput,
    )

    expensevp_toll = AllowedCommaDecimalField(
        decimal_places=2,
        max_digits=6,
        validators=[validators.MinValueValidator(Decimal('0.00'))],
        label='Frais de péage',
        help_text=(
            'Somme totale des frais de péage (en € TTC)'
        ),
        required=False,
        widget=forms.TextInput,
    )

    expensevp_parking = AllowedCommaDecimalField(
        decimal_places=2,
        max_digits=6,
        validators=[validators.MinValueValidator(Decimal('0.00'))],
        label='Frais de stationnement',
        help_text=(
            'Somme totale des frais de stationnement (en € TTC)'
        ),
        required=False,
        widget=forms.TextInput,
    )

    layouts = dict(
        start=material.Layout(
            material.Fieldset(
                'Votre région',
                'region',
            ),
        ),
        above=material.Layout(
            material.Fieldset(
                'Votre caisse d\'assurance maladie',
                'caisse',
            ),
        ),
        top=material.Layout(
            material.Fieldset(
                'Quel type de prescription médicale avez-vous reçue '
                'pour ce transport ?',
                'pmt_pel',
            ),
            material.Row(
                'pmt',
            ),
            material.Row(
                'pel',
            ),
            material.Row(
                'convocation',
            ),
        ),
        modevp=material.Layout(
            'modevp',
        ),
        vp_form=material.Layout(
            'distancevp',
            material.Row(
                'expensevp_toll',
                'expensevp_parking',
            ),
            'billvps',
        ),
        modeatp=material.Layout(
            'modeatp',
        ),
        atp_form=material.Layout(
            'expenseatp',
            'billatps',
        ),
    )

    class Meta:
        model = MRSRequest
        fields = [
            'caisse',
            'expenseatp',
            'expensevp_parking',
            'expensevp_toll',
            'distancevp',
            'modevp',
            'modeatp',
            'pel',
            'convocation',
        ]
        widgets = dict(
            distancevp=forms.TextInput,
            expensevp_toll=forms.TextInput,
            expensevp_parking=forms.TextInput,
        )

    def __init__(self, *args, **kwargs):
        kwargs.setdefault('initial', {})
        initial = kwargs['initial']

        kwargs['initial'].setdefault('expensevp_parking', 0)
        kwargs['initial'].setdefault('expensevp_toll', 0)

        if 'mrsrequest_uuid' in kwargs:
            mrsrequest_uuid = kwargs.pop('mrsrequest_uuid')
            instance = kwargs.get('instance')
            if not instance:
                kwargs['instance'] = MRSRequest()
            kwargs['instance'].id = mrsrequest_uuid
        elif 'instance' in kwargs:
            mrsrequest_uuid = str(kwargs['instance'].id)
        else:
            raise Exception('No instance, no uuid, secure it yourself')

        initial['mrsrequest_uuid'] = mrsrequest_uuid

        data, files, args, kwargs = self.args_extract(args, kwargs)

        if data:
            data, files = self.data_attachments(data, files, mrsrequest_uuid)

        kwargs['data'] = data
        kwargs['files'] = files
        super().__init__(*args, **kwargs)

    def cleaned_pmt_pel(self, cleaned_data):
        pmt_pel = cleaned_data.get('pmt_pel', 'pmt')

        if pmt_pel == 'pmt':
            cleaned_data.pop('pel', None)
            if not cleaned_data.get('pmt'):
                self.add_error('pmt', 'Merci de sélectionner votre PMT')

        elif pmt_pel == 'pel':
            if not cleaned_data.get('pel'):
                self.add_error('pel', 'Merci de saisir votre numéro de PMET')

        elif pmt_pel == 'convocation':
            if not cleaned_data.get('convocation'):
                self.add_error(
                    'convocation',
                    'Merci de saisir votre date de convocation'
                )

    def cleaned_vp_atp(self, cleaned_data):
        vp = cleaned_data.get('modevp')
        atp = cleaned_data.get('modeatp')

        if not vp and not atp:
            self.add_error(
                'modeatp',
                'Merci de choisir véhicule personnel et / ou transports en'
                ' commun',
            )
            self.add_error(
                'modevp',
                'Merci de choisir véhicule personnel et / ou transports en'
                ' commun',
            )

        if vp:
            distancevp = cleaned_data.get('distancevp')
            if not distancevp:
                self.add_error(
                    'distancevp',
                    'Merci de saisir la distance du trajet',
                )

            expensevp_toll = cleaned_data.get('expensevp_toll')
            expensevp_parking = cleaned_data.get('expensevp_parking')
            billvps = cleaned_data.get('billvps')
            if (expensevp_toll or expensevp_parking) and not billvps:
                self.add_error(
                    'billvps',
                    'Merci de soumettre vos justificatifs de transport'
                )

        if atp:
            billatps = cleaned_data.get('billatps')
            if not billatps:
                self.add_error(
                    'billatps',
                    'Merci de fournir les justificatifs de transport',
                )

            expenseatp = cleaned_data.get('expenseatp')
            if not expenseatp:
                self.add_error(
                    'expenseatp',
                    'Merci de saisir le total du coût de transports en commun',
                )

    def clean(self):
        cleaned_data = super().clean()

        self.cleaned_pmt_pel(cleaned_data)
        self.cleaned_vp_atp(cleaned_data)

        return cleaned_data

    def data_attachments(self, data, files, mrsrequest_uuid):
        data['pmt'] = PMT.objects.recorded_uploads(mrsrequest_uuid)
        data['billvps'] = BillVP.objects.recorded_uploads(mrsrequest_uuid)
        data['billatps'] = BillATP.objects.recorded_uploads(mrsrequest_uuid)

        if files:
            files.update(data)
        else:
            files = data
        return data, files

    def args_extract(self, args, kwargs):
        """Extract data and files args, return mutable objects."""
        # make popable (can't pop tuple of args)
        args = list(args)

        def getarg(name, num):
            if args and len(args) > num:
                return args.pop(num)
            elif kwargs.get('files'):
                return kwargs.pop('files')
            return None

        # First to not affect data = args.pop(0)
        files = getarg('files', 1)
        data = getarg('data', 0)

        # make mutable if something
        if files:
            files = MultiValueDict(files)
        if data:
            data = MultiValueDict(data)

        return data, files, args, kwargs

    def save(self, commit=True):
        if self.cleaned_data.get('parking_expensevp', None):
            self.instance.expensevp += self.cleaned_data.get(
                'parking_expensevp')

        obj = super().save(commit=commit)

        def save_attachments(form, obj):
            if form.cleaned_data.get('pmt_pel') == 'pmt':
                obj.save_pmt()
            else:
                obj.delete_pmt()
            obj.save_bills()

        save_m2m = getattr(self, 'save_m2m', None)
        if save_m2m:
            def _save_m2m():
                save_attachments(self, obj)
                save_m2m()

            self.save_m2m = _save_m2m
        else:
            save_attachments(self, obj)
        return obj
Exemple #20
0
class StudyForm(models.Model):
  number = models.PositiveIntegerField(default = 1, validators = [ validators.MaxValueValidator(11), validators.MinValueValidator(1), ])
  designator = models.TextField()
  objects = StudyFormManager()

  def __unicode__(self):
    return str(self.number) + self.designator

  def __str__(self):
    return str(self.number) + self.designator

  def get_url(self):
    return reverse('form', kwargs={'id': self.id,})
class User(AbstractUser):
    full_name = models.CharField(max_length=180)
    email = models.EmailField(blank=True)
    phone_number = models.CharField(max_length=16, blank=True)

    department = models.CharField(
        max_length=3,
        choices=DepartmentType.choices,
        default=DepartmentType.CSE.value,
    )

    # Only for teachers
    designation = models.CharField(
        _('designation'),
        max_length=256,
        null=True,
        blank=True,
    )
    qualification = models.CharField(
        _('qualification'),
        max_length=512,
        null=True,
        blank=True,
    )
    profile_picture = models.ImageField(
        upload_to=generate_propic_upload_location,
        null=True,
        blank=True,
    )
    cv_document = models.FileField(
        upload_to=generate_cv_upload_location,
        null=True,
        blank=True,
    )
    is_teacher = models.BooleanField(
        _('teacher status'),
        help_text=_(
            'Designates whether this user should be treated as teacher.'
        ),
        default=False,
    )
    is_external = models.BooleanField(
        _('external teacher status'),
        help_text=_(
            'Designates whether this teacher should be treated as an external teacher.'
        ),
        default=False,
    )
    cgpa = models.DecimalField(
        null=True,
        blank=True,
        decimal_places=2,
        max_digits=5,
        validators=[
            validators.MinValueValidator(2),
            validators.MaxValueValidator(4),
        ]
    )
    is_student = models.BooleanField(
        _('student status'),
        help_text=_(
            'Designates whether this user should be treated as student.'),
        default=False,
    )
    is_superuser = models.BooleanField(
        _('Admin'),
        help_text=_(
            'Designates whether this user should be treated as admin.'),
        default=False,
    )
    studentgroup = models.ForeignKey(
        'thesis.StudentGroup',
        related_name='students',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
    )

    class Meta:
        ordering = ['username']

    def studentgroup_count_by_batch(self, batch):
        return self.studentgroups.filter(
            approved=True,
            batch=batch,
        ).count()

    def __str__(self):
        name = self.username
        if self.full_name:
            name += f' - {self.full_name}'
        return name
Exemple #22
0
class LessonTiming(models.Model):
  number = models.PositiveIntegerField(default = 1, validators = [ validators.MaxValueValidator(8), validators.MinValueValidator(1), ])
  start = models.TimeField()
  end = models.TimeField()
Exemple #23
0
class Peak(models.Model):
    location = models.PointField(null=False, blank=False)
    elevation = models.FloatField(null=False,
                                  blank=False,
                                  validators=[validators.MinValueValidator(0)])
    name = models.CharField(max_length=128, null=False, blank=False)
Exemple #24
0
class Alarm(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(
        User,
        related_name='alarms',
        on_delete=models.CASCADE
    )
    origin = models.CharField(
        "Enderço de origem da carona",
        max_length=500
    )
    origin_point = models.PointField("Coordenadas de origem da carona")
    origin_radius = models.FloatField(
        "Raio de pesquisa da origem em km",
        default=5,
        validators=[validators.MaxValueValidator(10), validators.MinValueValidator(0.05)],
    )
    destination = models.CharField(
        "Enderço de destino da carona",
        max_length=500
    )
    destination_point = models.PointField("Coordenadas de destino da carona")
    destination_radius = models.FloatField(
        "Raio de pesquisa do destino em km",
        default=5,
        validators=[validators.MaxValueValidator(20), validators.MinValueValidator(0.05)],
    )
    price = models.PositiveSmallIntegerField("Preço máximo da carona em reais", null=True)
    auto_approve = models.NullBooleanField("Aprovação automática de passageiros", null=True)
    datetime_lte = models.DateTimeField("Datetime máxima de saída da carona", null=True)
    datetime_gte = models.DateTimeField("Datetime mínima de saída da carona", null=True)
    min_seats = models.PositiveSmallIntegerField(
        "Mínimo número de assentos na carona",
        validators=[validators.MaxValueValidator(10)],
        null=True
    )

    @classmethod
    def find_and_send(cls, trip):
        """Find and send alarms
        Takes a newly created trip and searches
        through all alarms to find matches.
        If any are found, send them.
        """

        # Start by filtering the easy ones
        # and then filter using expensive fields
        alarms = cls.objects.exclude(
            # Alarms that already ended should not be queried
            Q(datetime_lte__isnull=False) & Q(datetime_lte__lte=timezone.now())
        ).filter(
            # If the alarm defined auto_approve, filter it
            Q(auto_approve__isnull=True) | Q(auto_approve=trip.auto_approve)
        ).filter(
            # If the alarm defined price, filter it
            Q(price__isnull=True) | Q(price__gte=trip.price)
        ).filter(
            # If the alarm defined min_seats, filter it
            Q(min_seats__isnull=True) | Q(min_seats__lte=trip.max_seats)
        ).filter(
            # If the alarm defined datetime_gte, filter it
            Q(datetime_gte__isnull=True) | Q(datetime_gte__lte=trip.datetime)
        ).filter(
            # If the alarm defined datetime_lte, filter it
            Q(datetime_lte__isnull=True) | Q(datetime_lte__gte=trip.datetime)
        ).annotate(
            # First annotate the distances, since django can't compute
            # F expressions inside D functions, per
            # https://gis.stackexchange.com/questions/176735/geodjango-distance-filter-based-on-database-column
            origin_distance=Distance('origin_point', trip.origin_point),
            destination_distance=Distance('destination_point', trip.destination_point)
        ).filter(
            # Filter origin
            origin_distance__lte=F('origin_radius') * 1000
        ).filter(
            # Filter destination
            destination_distance__lte=F('destination_radius') * 1000
        )
        alarm_webhooks.MultipleAlarmsWebhook(alarms, trip).send()
        # Clear selected alarms
        alarms.delete()
Exemple #25
0
class Pet(models.Model):
    """Main pet description"""
    """
    - питомец:
        - кличка
        - возраст
        - пол
        (от до (интервалы))
        - привит, не прививки
    - более подробный профиль питомца
    """
    id = models.UUIDField(default=uuid.uuid4, primary_key=True)
    name = models.CharField(max_length=256, verbose_name="Кличка")
    age = models.IntegerField(verbose_name="Возраст",
                              validators=[
                                  validators.MinValueValidator(0),
                                  validators.MaxValueValidator(100)
                              ])
    doc = models.ForeignKey('petdocs.Registration',
                            on_delete=models.CASCADE,
                            verbose_name="Регистрационный документ",
                            related_name="pet_registration")
    photo = models.ImageField(upload_to="img",
                              blank=True,
                              verbose_name=_("Фото"))
    species = models.ForeignKey('pets.Species',
                                on_delete=models.CASCADE,
                                verbose_name="Вид животного")
    breed = models.ForeignKey('pets.Breed',
                              on_delete=models.CASCADE,
                              verbose_name="Порода")
    owner = models.ForeignKey("petdocs.Owner",
                              on_delete=models.CASCADE,
                              verbose_name="Владелец",
                              blank=True,
                              null=True)
    status = models.ForeignKey("pets.PetStatus",
                               on_delete=models.CASCADE,
                               verbose_name="Статус питомца",
                               blank=True,
                               null=True)
    gender = models.ForeignKey('pets.Gender',
                               on_delete=models.CASCADE,
                               verbose_name="Пол")

    def save(self,
             force_insert=False,
             force_update=False,
             using=None,
             update_fields=None):
        self.doc.reg_num = self.breed.name[0] + self.doc.date.strftime(
            "%d%m%Y")
        self.doc.save()
        super().save()

    def make_word_end(self):
        word = "лет"
        n = self.age
        if n > 99:
            n = n % 100
        if n in range(5, 21):
            word = "лет"
        elif n % 10 == 1:
            word = "год"
        elif n % 10 in range(2, 5):
            word = "года"
        return "{} {}".format(self.age, word)

    def __str__(self):
        return "{} ({}, {})".format(self.name, self.breed,
                                    self.make_word_end())

    def get_absolute_url(self):
        return reverse('pet-detail', kwargs={'pk': self.pk})
Exemple #26
0
class Event(models.Model, metaclass=ModelTranslateMeta):
    """Describes an event"""

    CATEGORY_ALUMNI = "alumni"
    CATEGORY_EDUCATION = "education"
    CATEGORY_CAREER = "career"
    CATEGORY_LEISURE = "leisure"
    CATEGORY_ASSOCIATION = "association"
    CATEGORY_OTHER = "other"

    EVENT_CATEGORIES = (
        (CATEGORY_ALUMNI, _("Alumni")),
        (CATEGORY_EDUCATION, _("Education")),
        (CATEGORY_CAREER, _("Career")),
        (CATEGORY_LEISURE, _("Leisure")),
        (CATEGORY_ASSOCIATION, _("Association Affairs")),
        (CATEGORY_OTHER, _("Other")),
    )

    DEFAULT_NO_REGISTRATION_MESSAGE = _("No registration required / "
                                        "Geen aanmelding vereist")

    title = MultilingualField(models.CharField, _("title"), max_length=100)

    description = MultilingualField(
        HTMLField,
        _("description"),
        help_text=_("Please fill in both of the description boxes (EN/NL),"
                    " even if your event is Dutch only! Fill in the English "
                    "description in Dutch then."),
    )

    start = models.DateTimeField(_("start time"))

    end = models.DateTimeField(_("end time"))

    organiser = models.ForeignKey("activemembers.MemberGroup",
                                  models.PROTECT,
                                  verbose_name=_("organiser"))

    category = models.CharField(
        max_length=40,
        choices=EVENT_CATEGORIES,
        verbose_name=_("category"),
        help_text=_("Alumni: Events organised for alumni, "
                    "Education: Education focused events, "
                    "Career: Career focused events, "
                    "Leisure: borrels, parties, game activities etc., "
                    "Association Affairs: general meetings or "
                    "any other board related events, "
                    "Other: anything else."),
    )

    registration_start = models.DateTimeField(
        _("registration start"),
        null=True,
        blank=True,
        help_text=_("If you set a registration period registration will be "
                    "required. If you don't set one, registration won't be "
                    "required. Prefer times when people don't have lectures, "
                    "e.g. 12:30 instead of 13:37."),
    )

    registration_end = models.DateTimeField(
        _("registration end"),
        null=True,
        blank=True,
        help_text=_("If you set a registration period registration will be "
                    "required. If you don't set one, registration won't be "
                    "required."),
    )

    cancel_deadline = models.DateTimeField(_("cancel deadline"),
                                           null=True,
                                           blank=True)

    send_cancel_email = models.BooleanField(
        _("send cancellation notifications"),
        default=True,
        help_text=_("Send an email to the organising party when a member "
                    "cancels their registration after the deadline."),
    )

    location = MultilingualField(
        models.CharField,
        _("location"),
        max_length=255,
    )

    map_location = models.CharField(
        _("location for minimap"),
        max_length=255,
        help_text=_("Location of Huygens: Heyendaalseweg 135, Nijmegen. "
                    "Location of Mercator 1: Toernooiveld 212, Nijmegen. "
                    "Not shown as text!!"),
    )

    price = models.DecimalField(
        _("price"),
        max_digits=5,
        decimal_places=2,
        default=0,
        validators=[validators.MinValueValidator(0)],
    )

    fine = models.DecimalField(
        _("fine"),
        max_digits=5,
        decimal_places=2,
        default=0,
        # Minimum fine is checked in this model's clean(), as it is only for
        # events that require registration.
        help_text=_("Fine if participant does not show up (at least €5)."),
        validators=[validators.MinValueValidator(0)],
    )

    max_participants = models.PositiveSmallIntegerField(
        _("maximum number of participants"),
        blank=True,
        null=True,
    )

    no_registration_message = MultilingualField(
        models.CharField,
        _("message when there is no registration"),
        max_length=200,
        blank=True,
        null=True,
        help_text=(format_lazy("{} {}", _("Default:"),
                               DEFAULT_NO_REGISTRATION_MESSAGE)),
    )

    published = models.BooleanField(_("published"), default=False)

    registration_reminder = models.ForeignKey(
        ScheduledMessage,
        on_delete=models.deletion.SET_NULL,
        related_name="registration_event",
        blank=True,
        null=True,
    )
    start_reminder = models.ForeignKey(
        ScheduledMessage,
        on_delete=models.deletion.SET_NULL,
        related_name="start_event",
        blank=True,
        null=True,
    )

    documents = models.ManyToManyField(
        "documents.Document",
        verbose_name=_("documents"),
        blank=True,
    )

    slide = models.ForeignKey(
        Slide,
        verbose_name="slide",
        help_text=_("Change the header-image on the event's info-page to one "
                    "specific to this event."),
        blank=True,
        on_delete=models.deletion.SET_NULL,
        null=True,
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._price = self.price
        self._registration_start = self.registration_start

    @property
    def after_cancel_deadline(self):
        return self.cancel_deadline and self.cancel_deadline <= timezone.now()

    @property
    def registration_started(self):
        return self.registration_start <= timezone.now()

    @property
    def registration_required(self):
        return bool(self.registration_start) or bool(self.registration_end)

    def has_fields(self):
        return self.registrationinformationfield_set.count() > 0

    def reached_participants_limit(self):
        """Is this event up to capacity?"""
        return (self.max_participants is not None and self.max_participants <=
                self.eventregistration_set.filter(date_cancelled=None).count())

    @property
    def registrations(self):
        """Queryset with all non-cancelled registrations"""
        return self.eventregistration_set.filter(date_cancelled=None)

    @property
    def participants(self):
        """Return the active participants"""
        if self.max_participants is not None:
            return self.registrations.order_by("date")[:self.max_participants]
        return self.registrations.order_by("date")

    @property
    def queue(self):
        """Return the waiting queue"""
        if self.max_participants is not None:
            return self.registrations.order_by("date")[self.max_participants:]
        return []

    @property
    def cancellations(self):
        """Return a queryset with the cancelled events"""
        return self.eventregistration_set.exclude(
            date_cancelled=None).order_by("date_cancelled")

    @property
    def registration_allowed(self):
        now = timezone.now()
        return (bool(self.registration_start or self.registration_end)
                and self.registration_end > now >= self.registration_start)

    @property
    def cancellation_allowed(self):
        now = timezone.now()
        return (bool(self.registration_start or self.registration_end)
                and self.registration_start <= now < self.start)

    def is_pizza_event(self):
        try:
            self.pizzaevent
            return True
        except ObjectDoesNotExist:
            return False

    def clean(self):
        super().clean()
        errors = {}
        if self.start is None:
            errors.update(
                {"start": _("Start cannot have an empty date or time field")})
        if self.end is None:
            errors.update(
                {"end": _("End cannot have an empty date or time field")})
        if self.start is not None and self.end is not None:
            if self.end < self.start:
                errors.update(
                    {"end": _("Can't have an event travel back in time")})
            if self.registration_required:
                if self.fine < 5:
                    errors.update({
                        "fine":
                        _("The fine for this event is too low "
                          "(must be at least €5).")
                    })
                for lang in settings.LANGUAGES:
                    field = "no_registration_message_" + lang[0]
                    if getattr(self, field):
                        errors.update({
                            field:
                            _("Doesn't make sense to have this "
                              "if you require registrations.")
                        })
                if not self.registration_start:
                    errors.update({
                        "registration_start":
                        _("If registration is required, you need a start of "
                          "registration")
                    })
                if not self.registration_end:
                    errors.update({
                        "registration_end":
                        _("If registration is required, you need an end of "
                          "registration")
                    })
                if not self.cancel_deadline:
                    errors.update({
                        "cancel_deadline":
                        _("If registration is required, "
                          "you need a deadline for the cancellation")
                    })
                elif self.cancel_deadline > self.start:
                    errors.update({
                        "cancel_deadline":
                        _("The cancel deadline should be"
                          " before the start of the event.")
                    })
                if (self.registration_start and self.registration_end and
                    (self.registration_start >= self.registration_end)):
                    message = _("Registration start should be before "
                                "registration end")
                    errors.update({
                        "registration_start": message,
                        "registration_end": message
                    })

        try:
            if (self.organiser is not None and self.send_cancel_email
                    and self.organiser.contact_mailinglist is None):
                errors.update({
                    "send_cancel_email":
                    _("This organiser does not "
                      "have a contact mailinglist.")
                })
        except ObjectDoesNotExist:
            pass

        if self.published:
            if (self.price != self._price and self._registration_start
                    and self._registration_start <= timezone.now()):
                errors.update({
                    "price":
                    _("You cannot change this field after "
                      "the registration has started.")
                })
            if (self._registration_start
                    and self.registration_start != self._registration_start
                    and self._registration_start <= timezone.now()):
                errors.update({
                    "registration_start":
                    _("You cannot change this field after "
                      "the registration has started.")
                })

        if errors:
            raise ValidationError(errors)

    def get_absolute_url(self):
        return reverse("events:event", args=[str(self.pk)])

    def save(self, *args, **kwargs):
        delete_collector = Collector(
            using=router.db_for_write(self.__class__, instance=self))

        if not self.pk:
            super().save(*args, **kwargs)

        if self.published:
            if self.registration_required:
                registration_reminder_time = (self.registration_start -
                                              timezone.timedelta(hours=1))
                registration_reminder = ScheduledMessage()
                if (self.registration_reminder is not None
                        and not self.registration_reminder.sent):
                    registration_reminder = self.registration_reminder

                if registration_reminder_time > timezone.now():
                    registration_reminder.title_en = "Event registration"
                    registration_reminder.title_nl = "Evenement registratie"
                    registration_reminder.body_en = ("Registration for '{}' "
                                                     "starts in 1 hour".format(
                                                         self.title_en))
                    registration_reminder.body_nl = ("Registratie voor '{}' "
                                                     "start in 1 uur".format(
                                                         self.title_nl))
                    registration_reminder.category = Category.objects.get(
                        key=Category.EVENT)
                    registration_reminder.time = registration_reminder_time
                    registration_reminder.url = (
                        f"{settings.BASE_URL}"
                        f'{reverse("events:event", args=[self.id])}')

                    registration_reminder.save()
                    self.registration_reminder = registration_reminder
                    self.registration_reminder.users.set(
                        Member.current_members.all())
                elif registration_reminder.pk is not None:
                    delete_collector.add([self.registration_reminder])
                    self.registration_reminder = None

            start_reminder_time = self.start - timezone.timedelta(hours=1)
            start_reminder = ScheduledMessage()
            if self.start_reminder is not None and not self.start_reminder.sent:
                start_reminder = self.start_reminder

            if start_reminder_time > timezone.now():
                start_reminder.title_en = "Event"
                start_reminder.title_nl = "Evenement"
                start_reminder.body_en = f"'{self.title_en}' starts in " "1 hour"
                start_reminder.body_nl = f"'{self.title_nl}' begint over " "1 uur"
                start_reminder.category = Category.objects.get(
                    key=Category.EVENT)
                start_reminder.time = start_reminder_time
                start_reminder.save()
                self.start_reminder = start_reminder
                if self.registration_required:
                    self.start_reminder.users.set(
                        [r.member for r in self.participants if r.member])
                else:
                    self.start_reminder.users.set(Member.current_members.all())
            elif start_reminder.pk is not None:
                delete_collector.add([self.start_reminder])
                self.start_reminder = None
        else:
            if (self.registration_reminder is not None
                    and not self.registration_reminder.sent):
                delete_collector.add([self.registration_reminder])
                self.registration_reminder = None

            if self.start_reminder is not None and not self.start_reminder.sent:
                delete_collector.add([self.start_reminder])
                self.start_reminder = None

        super().save()
        delete_collector.delete()

    def delete(self, using=None, keep_parents=False):
        using = using or router.db_for_write(self.__class__, instance=self)
        collector = Collector(using=using)
        collector.collect([self], keep_parents=keep_parents)

        if (self.registration_reminder is not None
                and not self.registration_reminder.sent):
            collector.add([self.registration_reminder])
        if self.start_reminder is not None and not self.start_reminder.sent:
            collector.add([self.start_reminder])
        if self.is_pizza_event():
            collector.add([self.pizzaevent])

        return collector.delete()

    def __str__(self):
        return "{}: {}".format(
            self.title,
            timezone.localtime(self.start).strftime("%Y-%m-%d %H:%M"))

    class Meta:
        ordering = ("-start", )
        permissions = (("override_organiser",
                        "Can access events as if organizing"), )
Exemple #27
0
class TeacherClassRegForm(FormWithRequiredCss):
    location_choices = [    (True, "I will use my own space for this class (e.g. space in my laboratory).  I have explained this in 'Message for Directors' below."),
                            (False, "I would like a classroom to be provided for my class.")]
    lateness_choices = [    (True, "Students may join this class up to 20 minutes after the official start time."),
                            (False, "My class is not suited to late additions.")]
    hardness_choices = [
        ("*",    "*    - Should be understandable to everyone in the class.",),
        ("**",   "**   - Should not be too difficult for most students.",),
        ("***",  "***  - Will move quickly and will have many difficult parts.",),
        ("****", "**** - You should not expect to be able to understand most of this class.",),
    ]

    # The following is a dummy list (because using None causes an error). To enable class styles, admins should set the
    # Tag class_style_choices with value in the following JSON format, where the first element of each list
    # is the value stored in the database, and the second value is the option shown on the form.
    #     [["Lecture", "Lecture Style Class"], ["Seminar", "Seminar Style Class"]]
    style_choices = []

    # Grr, TypedChoiceField doesn't seem to exist yet
    title          = StrippedCharField(    label='Course Title', length=50, max_length=200 )
    category       = forms.ChoiceField( label='Course Category', choices=[], widget=BlankSelectWidget() )
    class_info     = StrippedCharField(   label='Course Description', widget=forms.Textarea(),
                                        help_text='<span class="tex2jax_ignore">Want to enter math? Use <tt>$$ Your-LaTeX-code-here $$</tt>. (e.g. use $$\pi$$ to mention &pi;)</span>' )
    prereqs        = forms.CharField(   label='Course Prerequisites', widget=forms.Textarea(attrs={'rows': 4}), required=False,
                                        help_text='If your course does not have prerequisites, leave this box blank.')

    duration       = forms.ChoiceField( label='Duration of a Class Meeting', help_text='(hours:minutes)', choices=[('0.0', 'Program default')], widget=BlankSelectWidget() )
    num_sections   = forms.ChoiceField( label='Number of Sections', choices=[(1,1)], widget=BlankSelectWidget(),
                                        help_text='(How many independent sections (copies) of your class would you like to teach?)' )
    session_count  = forms.ChoiceField( label='Number of Days of Class', choices=[(1,1)], widget=BlankSelectWidget(),
                                        help_text='(How many days will your class take to complete?)' )

    # To enable grade ranges, admins should set the Tag grade_ranges.
    # e.g. [[7,9],[9,10],[9,12],[10,12],[11,12]] gives five grade ranges: 7-9, 9-10, 9-12, 10-12, and 11-12
    grade_range    = forms.ChoiceField( label='Grade Range', choices=[], required=False, widget=BlankSelectWidget() )
    grade_min      = forms.ChoiceField( label='Minimum Grade Level', choices=[(7, 7)], widget=BlankSelectWidget() )
    grade_max      = forms.ChoiceField( label='Maximum Grade Level', choices=[(12, 12)], widget=BlankSelectWidget() )
    class_size_max = forms.ChoiceField( label='Maximum Number of Students',
                                        choices=[(0, 0)],
                                        widget=BlankSelectWidget(),
                                        validators=[validators.MinValueValidator(1)],
                                        help_text='The above class-size and grade-range values are absolute, not the "optimum" nor "recommended" amounts. We will not allow any more students than you specify, nor allow any students in grades outside the range that you specify. Please contact us later if you would like to make an exception for a specific student.' )
    class_size_optimal = forms.IntegerField( label='Optimal Number of Students', help_text="This is the number of students you would have in your class in the most ideal situation.  This number is not a hard limit, but we'll do what we can to try to honor this." )
    optimal_class_size_range = forms.ChoiceField( label='Optimal Class Size Range', choices=[(0, 0)], widget=BlankSelectWidget() )
    allowable_class_size_ranges = forms.MultipleChoiceField( label='Allowable Class Size Ranges', choices=[(0, 0)], widget=forms.CheckboxSelectMultiple(),
                                                             help_text="Please select all class size ranges you are comfortable teaching." )
    class_style = forms.ChoiceField( label='Class Style', choices=style_choices, required=False, widget=BlankSelectWidget())
    hardness_rating = forms.ChoiceField( label='Difficulty',choices=hardness_choices, initial="**",
        help_text="Which best describes how hard your class will be for your students?")
    allow_lateness = forms.ChoiceField( label='Punctuality', choices=lateness_choices, widget=forms.RadioSelect() )

    requested_room = forms.CharField(   label='Room Request', required=False,
                                        help_text='If you have a specific room or type of room in mind, name a room at %s that would be ideal for you.' % settings.INSTITUTION_NAME )

    requested_special_resources = forms.CharField( label='Special Requests', widget=forms.Textarea(), required=False,
                                                   help_text="Write in any specific resources you need, like a piano, empty room, or kitchen. We cannot guarantee you any of the special resources you request, but we will contact you if we are unable to get you the resources you need. Please include any necessary explanations in the 'Message for Directors' box! " )

    purchase_requests = forms.CharField( label='Planned Purchases', widget=forms.Textarea(), required=False,
                                         help_text='We give all teachers a $30 budget per class section for their classes; we can reimburse you if you turn in an itemized receipt with attached reimbursement form before the end of the program.  If you would like to exceed this budget, please type a budget proposal here stating what you would like to buy, what it will cost, and why you would like to purchase it.' )

    message_for_directors       = forms.CharField( label='Message for Directors', widget=forms.Textarea(), required=False,
                                                   help_text='Please explain any special circumstances and equipment requests. Remember that you can be reimbursed for up to $30 (or more with the directors\' approval) for class expenses if you submit itemized receipts.' )


    def __init__(self, crmi, *args, **kwargs):
        from esp.program.controllers.classreg import get_custom_fields

        def hide_field(field, default=None):
            field.widget = forms.HiddenInput()
            if default is not None:
                field.initial = default
        def hide_choice_if_useless(field):
            """ Hide a choice field if there's only one choice """
            if len(field.choices) == 1:
                hide_field(field, default=field.choices[0][0])

        super(TeacherClassRegForm, self).__init__(*args, **kwargs)

        prog = crmi.program

        section_numbers = crmi.allowed_sections_actual
        section_numbers = zip(section_numbers, section_numbers)

        class_sizes = crmi.getClassSizes()
        class_sizes = zip(class_sizes, class_sizes)

        class_grades = crmi.getClassGrades()
        class_grades = zip(class_grades, class_grades)

        class_ranges = ClassSizeRange.get_ranges_for_program(prog)
        class_ranges = [(range.id, range.range_str()) for range in class_ranges]

        # num_sections: section_list; hide if useless
        self.fields['num_sections'].choices = section_numbers
        hide_choice_if_useless( self.fields['num_sections'] )
        # category: program.class_categories.all()
        self.fields['category'].choices = [ (x.id, x.category) for x in prog.class_categories.all() ]
        # grade_min, grade_max: crmi.getClassGrades
        self.fields['grade_min'].choices = class_grades
        self.fields['grade_max'].choices = class_grades
        if Tag.getTag('grade_ranges'):
            grade_ranges = json.loads(Tag.getTag('grade_ranges'))
            self.fields['grade_range'].choices = [(range,str(range[0]) + " - " + str(range[1])) for range in grade_ranges]
            self.fields['grade_range'].required = True
            hide_field( self.fields['grade_min'] )
            self.fields['grade_min'].required = False
            hide_field( self.fields['grade_max'] )
            self.fields['grade_max'].required = False
        else:
            hide_field( self.fields['grade_range'] )
        if crmi.use_class_size_max:
            # class_size_max: crmi.getClassSizes
            self.fields['class_size_max'].choices = class_sizes
        else:
            del self.fields['class_size_max']

        if Tag.getBooleanTag('use_class_size_optimal', default=False):
            if not crmi.use_class_size_optimal:
                del self.fields['class_size_optimal']

            if crmi.use_optimal_class_size_range:
                self.fields['optimal_class_size_range'].choices = class_ranges
            else:
                del self.fields['optimal_class_size_range']

            if crmi.use_allowable_class_size_ranges:
                self.fields['allowable_class_size_ranges'].choices = class_ranges
            else:
                del self.fields['allowable_class_size_ranges']
        else:
            del self.fields['class_size_optimal']
            del self.fields['optimal_class_size_range']
            del self.fields['allowable_class_size_ranges']

        # decide whether to display certain fields

        # prereqs
        if not crmi.set_prereqs:
            self.fields['prereqs'].widget = forms.HiddenInput()

        # allow_lateness
        if not crmi.allow_lateness:
            self.fields['allow_lateness'].widget = forms.HiddenInput()
            self.fields['allow_lateness'].initial = 'False'

        self.fields['duration'].choices = sorted(crmi.getDurations())
        hide_choice_if_useless( self.fields['duration'] )

        # session_count
        if crmi.session_counts:
            session_count_choices = crmi.session_counts_ints
            session_count_choices = zip(session_count_choices, session_count_choices)
            self.fields['session_count'].choices = session_count_choices
        hide_choice_if_useless( self.fields['session_count'] )

        # requested_room
        if not crmi.ask_for_room:
            hide_field( self.fields['requested_room'] )

        #   Hide resource fields since separate forms are now being used. - Michael P
        #   Most have now been removed, but this one gets un-hidden by open classes.
        self.fields['requested_special_resources'].widget = forms.HiddenInput()

        #   Add program-custom form components (for inlining additional questions without
        #   introducing a separate program module)
        custom_fields = get_custom_fields()
        for field_name in custom_fields:
            self.fields[field_name] = custom_fields[field_name]

        #   Modify help text on these fields if necessary.
        #   TODO(benkraft): Is there a reason not to allow this on all fields?
        custom_helptext_fields = [
            'duration', 'class_size_max', 'num_sections', 'requested_room',
            'message_for_directors', 'purchase_requests', 'class_info',
            'grade_max', 'grade_min'] + custom_fields.keys()
        for field in custom_helptext_fields:
            tag_data = Tag.getProgramTag('teacherreg_label_%s' % field, prog)
            if tag_data:
                self.fields[field].label = tag_data
            tag_data = Tag.getProgramTag('teacherreg_help_text_%s' % field, prog)
            if tag_data:
                self.fields[field].help_text = tag_data

        #   Hide fields as desired.
        tag_data = Tag.getProgramTag('teacherreg_hide_fields', prog)
        if tag_data:
            for field_name in tag_data.split(','):
                hide_field(self.fields[field_name])

        tag_data = Tag.getProgramTag('teacherreg_default_min_grade', prog)
        if tag_data:
            self.fields['grade_min'].initial = tag_data

        tag_data = Tag.getProgramTag('teacherreg_default_max_grade', prog)
        if tag_data:
            self.fields['grade_max'].initial = tag_data

        tag_data = Tag.getProgramTag('teacherreg_default_class_size_max', prog)
        if tag_data:
            self.fields['class_size_max'].initial = tag_data

        #   Rewrite difficulty label/choices if desired:
        if Tag.getTag('teacherreg_difficulty_choices'):
            self.fields['hardness_rating'].choices = json.loads(Tag.getTag('teacherreg_difficulty_choices'))

        # Get class_style_choices from tag, otherwise hide the field
        if Tag.getTag('class_style_choices'):
            self.fields['class_style'].choices = json.loads(Tag.getTag('class_style_choices'))
            self.fields['class_style'].required = True
        else:
            hide_field(self.fields['class_style'])
        # plus subprogram section wizard

    def clean(self):
        cleaned_data = self.cleaned_data

        # Make sure grade_min <= grade_max
        # We need to cast here until we can make the ChoiceFields into TypedChoiceFields.
        grade_min = cleaned_data.get('grade_min')
        grade_max = cleaned_data.get('grade_max')
        if grade_min and grade_max:
            grade_min = int(grade_min)
            grade_max = int(grade_max)
            if grade_min > grade_max:
                msg = u'Minimum grade must be less than the maximum grade.'
                self.add_error('grade_min', msg)
                self.add_error('grade_max', msg)

        # Make sure the optimal class size <= maximum class size.
        class_size_optimal = cleaned_data.get('class_size_optimal')
        class_size_max = cleaned_data.get('class_size_max')
        if class_size_optimal and class_size_max:
            class_size_optimal = int(class_size_optimal)
            class_size_max = int(class_size_max)
            if class_size_optimal > class_size_max:
                msg = u'Optimal class size must be less than or equal to the maximum class size.'
                self.add_error('class_size_optimal', msg)
                self.add_error('class_size_max', msg)

        if class_size_optimal == '':
            cleaned_data['class_size_optimal'] = None

        # If using grade ranges instead of min and max, extract min and max from grade range.
        if cleaned_data.get('grade_range'):
            cleaned_data['grade_min'], cleaned_data['grade_max'] = json.loads(cleaned_data.get('grade_range'))

        # Return cleaned data
        return cleaned_data

    def _get_total_time_requested(self):
        """ Get total time requested. Do not call before validation. """
        return float(self.cleaned_data['duration']) * int(self.cleaned_data['num_sections'])
class Squirrel(models.Model):
    Latitude=models.DecimalField(
            max_digits=16, 
            decimal_places=13,
            validators=[
                        validators.MaxValueValidator(41),
                        validators.MinValueValidator(40),
                        ],
            help_text=_('Latitude coordinate for squirrel sighting point'),
            )

    Longitude=models.DecimalField(
            max_digits=16,   
            decimal_places=13,
            validators=[
                        validators.MaxValueValidator(-73),
                        validators.MinValueValidator(-74),
                        ],
            help_text=_('Longitude coordinate for squirrel sighting point'),
            )

    Unique_Squirrel_ID=models.CharField(
            unique=True,
            max_length=20,
            validators=[validators.RegexValidator("\d{1,2}[A-I]-[PA]M-\d{4}-\d{2}",message='Please enter the right ID.')],
            help_text=_('Identification tag for each squirrel sightings. The tag is comprised of "Hectare ID" + "Shift" + "Date" + "Hectare Squirrel Number.'),
            )

    PM='PM'
    AM='AM'
    shift_choices=(
        (PM,'P.M.'),
        (AM,'A.M.'),
        )
    Shift=models.CharField(
        max_length=2,
        choices=shift_choices,
        help_text=_('Value is either "AM" or "PM," to communicate whether or not the sighting session occurred in the morning or late afternoon.'),
        )
    
    Date=models.DateField(
            help_text=_('Concatenation of the sighting session day,month and year.(formate: YYYY-MM-DD)'),
        )
    
    ADULT='Adult'
    JUVENILE='Juvenile'
    age_choices=(
        (ADULT,'adult'),
        (JUVENILE,'juvenile'),
        )
    Age=models.CharField(
        max_length=10,
        choices=age_choices,
        help_text=_('Value is either "adult" or "juvenile".'),
        blank=True,
        )
    
    GRAY='Gray'
    CINNAMON='Cinnamon'
    BLACK='Black'
    fur_choices=(
        (GRAY,'gray'),
        (CINNAMON,'cinnamon'),
        (BLACK,'black'),
        )
    Primary_Fur_Color=models.CharField(
        max_length=10,
        choices=fur_choices,
        help_text=_('Value is either "gray," "cinnamon" or "black".'),
        blank=True,
        )
    
    GROUND_PLANE='Ground Plane'
    ABOVE_GROUND='Above Ground'
    location_choices=(
            (GROUND_PLANE,'ground plane'),
            (ABOVE_GROUND,'above ground'),
            )
    Location=models.CharField(
            max_length=20,
            choices=location_choices,
            help_text=_('Value is either "ground plane" or "above ground." Sighters were instructed to indicate the location of where the squirrel was when first sighted.'),
            blank=True,
            )

    Specific_Location=models.CharField(
            max_length=100,
            help_text=_('Sighters occasionally added commentary on the squirrel location. These notes are provided here.'),
            blank=True,
            )

    Running=models.BooleanField(
            help_text=_('Squirrel was seen running.'),
            )

    Chasing=models.BooleanField(
            help_text=_('Squirrel was seen chasing another squirrel.'),
            )

    Climbing=models.BooleanField(
            help_text=_('Squirrel was seen climbing a tree or other environmental landmark.'),
            )

    Eating=models.BooleanField(
            help_text=_('Squirrel was seen eating.'),
            )

    Foraging=models.BooleanField(
            help_text=_('Squirrel was seen foraging for food.'),
            )

    Other_Activities=models.CharField(
            max_length=100,
            blank=True,
            )

    Kuks=models.BooleanField(
            help_text=_('Squirrel was heard kukking, a chirpy vocal communication used for a variety of reasons.'),
            )

    Quaas=models.BooleanField(
            help_text=_('Squirrel was heard quaaing, an elongated vocal communication which can indicate the presence of a ground predator such as a dog.'),
            )

    Moans=models.BooleanField(
            help_text=_('Squirrel was heard moaning, a high-pitched vocal communication which can indicate the presence of an air predator such as a hawk.'),
            )
    Tail_flags=models.BooleanField(
            help_text=_("Squirrel was seen flagging its tail. Flagging is a whipping motion used to exaggerate squirrel's size and confuse rivals or predators. Looks as if the squirrel is scribbling with tail into the air.")
            )

    Tail_twitches=models.BooleanField(
            help_text=_('Squirrel was seen twitching its tail. Looks like a wave running through the tail, like a breakdancer doing the arm wave. Often used to communicate interest, curiosity.'),
            )

    Approaches=models.BooleanField(
            help_text=_('Squirrel was seen approaching human, seeking food.'),
            )

    Indifferent=models.BooleanField(
            help_text=_('Squirrel was indifferent to human presence.'),
            )

    Runs_from=models.BooleanField(
            help_text=_('Squirrel was seen running from humans, seeing them as a threat.'),
            )

    def __str__(self):
        return self.Unique_Squirrel_ID
Exemple #29
0
class Light(models.Model):
    title = models.CharField(null=False, blank=False, max_length=32)
    topic = models.CharField(null=False,
                             blank=False,
                             max_length=64,
                             default="test_bulb/light/test_bulb/command")
    hcolor = ColorField(null=False,
                        blank=False,
                        help_text="Select color that fitd your mood.")
    brightness = models.FloatField(null=True,
                                   blank=True,
                                   validators=[
                                       validators.MaxValueValidator(255),
                                       validators.MinValueValidator(0)
                                   ],
                                   default=100)
    color_temperature = models.IntegerField(
        null=True,
        blank=True,
        validators=[
            validators.MaxValueValidator(6500),
            validators.MinValueValidator(2700)
        ],
        default=2700)
    color_from_temperature = models.BooleanField(null=False, blank=False)
    effect = models.CharField(null=True,
                              blank=True,
                              choices=[("rainbow", "Rainbow"),
                                       ("blink", "Blink")],
                              max_length=32)

    graphql_fields = [
        GraphQLString("title"),
        GraphQLString("topic"),
        GraphQLString("hcolor"),
        GraphQLString("brightness"),
        GraphQLString("color_temperature"),
        GraphQLString("color_from_temperature"),
        GraphQLString("effect"),
    ]

    search_fields = [
        index.SearchField("title"),
        index.SearchField("topic"),
        index.SearchField("hcolor"),
        index.SearchField("brightness"),
        index.SearchField("color_temperature"),
        index.SearchField("color_from_temperature"),
        index.SearchField("effect"),
    ]

    panels = [
        FieldPanel("title"),
        FieldPanel("topic"),
        FieldPanel("hcolor"),
        FieldPanel("brightness"),
        FieldPanel("color_temperature"),
        FieldPanel("color_from_temperature"),
        FieldPanel("effect"),
    ]

    def __str__(self):
        return f"{self.title}"

    def save(self, *args, **kwargs):
        client = mqtt.Client()

        hexc = self.hcolor.lstrip('#')
        rgbc = tuple(int(hexc[i:i + 2], 16) for i in (0, 2, 4))

        client.connect("10.1.0.1", 1883, 60)
        client.publish(self.topic,
                       '{"state":"ON",' + f'"brightness":{self.brightness},' +
                       '"color":{' +
                       f'"r":{rgbc[0]},"g":{rgbc[1]},"b":{rgbc[2]}' + '}}',
                       qos=1)
        # "white_value":255,"color_temp":370

        super(Light, self).save(*args, **kwargs)
class Stay(models.Model):
    '''
    This is the model field that stores information about a stay.
    By default, new stays are not approved. The only optional field is the
    additional questions or concerns field. Each stay entry is linked to one
    Address entry. When creating a Stay object manually, call the full_clean()
    method before the save() method is called and catch any ValidationErrors to 
    ensure the Stay is created with valid data.
    '''
    class Meta:
        ordering = ['is_approved', 'in_date', 'guest']
        verbose_name_plural = 'Stays'

    guest = models.ForeignKey(Guest, on_delete=models.PROTECT)
    in_date = models.DateField('check-in date', unique=True)
    out_date = models.DateField('check-out date', unique=True)
    is_using_custom_price = models.BooleanField(default=False)
    total_price = models.FloatField(default=0,
                                    validators=[
                                        validators.MinValueValidator(0),
                                    ])
    number_of_guests = models.PositiveIntegerField(
        default=1,
        validators=[
            validators.MinValueValidator(1),
            validators.MaxValueValidator(6)
        ])
    is_approved = models.BooleanField(default=False)
    is_fully_paid = models.BooleanField(default=False)
    additional_questions_or_concerns = models.TextField(default='', blank=True)

    def __str__(self):
        return f'{self.guest.name}: {"APPROVED" if self.is_approved else "PENDING"}'

    def clean(self):
        '''
        This is the partial overriden version of the models.Model's clean method.
        It performs additional validation to ensure data is sufficient. Then, once the
        data is validated, calculates the total cost of the stay depending on the seasons.
        '''
        super().clean()
        self._perform_additional_validation()
        if not self.is_using_custom_price:
            self._set_calculated_total_price()

    def _perform_additional_validation(self):
        self._ensure_checkout_occurs_before_checkin()
        self._ensure_global_setting_is_set()
        self._ensure_at_least_minimum_days_of_stay()
        self._ensure_no_conflicts_with_existing_stays()
        self._ensure_valid_number_of_guests()

    def _ensure_checkout_occurs_before_checkin(self):
        if self.in_date >= self.out_date:
            raise ValidationError(
                ('The check-in date must be before the check-out date.'))

    def _ensure_global_setting_is_set(self):
        if Globals.objects.all().count() != 1:
            raise ValidationError(('Administrative Error: Globals not set.'))

    def _ensure_at_least_minimum_days_of_stay(self):
        num_days = (self.out_date - self.in_date).days
        global_setting = Globals.objects.get(pk=1)
        if num_days < global_setting.minimum_days_of_stay:
            raise ValidationError((
                f'A minimum of {global_setting.minimum_days_of_stay} day(s) required.'
            ))

    def _ensure_no_conflicts_with_existing_stays(self):
        num_days = (self.out_date - self.in_date).days
        taken_dates = data.get_taken_dates()
        for x in range(0, num_days + 1):
            current_date = self.in_date + datetime.timedelta(days=x)
            current_date = datetime.datetime.combine(
                current_date,
                datetime.datetime.min.time()).astimezone(pytz.utc)
            print(current_date)
            for taken_date in taken_dates:
                print(f'Checking against {taken_date}')
                if current_date == taken_date:
                    raise ValidationError((
                        'One or more dates of stay conflict with a current approved visit.'
                    ))

    def _ensure_valid_number_of_guests(self):
        if self.number_of_guests < 1:
            raise ValidationError(('There must be at least one guest.'))

    def _set_calculated_total_price(self):
        num_days = (self.out_date - self.in_date).days
        self.total_price = 0
        for x in range(0, num_days):
            current_date = self.in_date + datetime.timedelta(days=x)
            price_for_day = self._get_rate(current_date)
            self.total_price += price_for_day
        global_obj = Globals.objects.get(pk=1)
        self.total_price += global_obj.cleaning_fee
        self.total_price *= (((global_obj.state_tax_rate_percent +
                               global_obj.county_tax_rate_percent) / 100.0) +
                             1)

    def _get_rate(self, date):
        price = Globals.objects.get(pk=1).default_price_per_night
        seasons = SeasonPricing.objects.all()
        for season in seasons:
            if date > season.start_date and date < season.end_date:
                price = season.price_per_night
        return price