Exemple #1
0
class NotCoveredFields(models.Model):
    json = JSONField()
Exemple #2
0
class CallbackEvent(models.Model):
    """Model used to log all callbacks."""
    # ref to the webhook that picked up the event
    webhook = models.ForeignKey(Webhook)
    # events are read-only so just a timestamp required
    timestamp = models.DateTimeField()
    # the Trello event type - moveCard, commentCard, etc.
    event_type = models.CharField(max_length=50)
    # the complete request payload, as JSON
    event_payload = JSONField()

    def __unicode__(self):
        if self.id:
            return ("CallbackEvent %i: '%s' raised by webhook %s." %
                    (self.id, self.event_type, self.webhook.id))
        else:
            return ("CallbackEvent: '%s' raised by webhook %s." %
                    (self.event_type, self.webhook.id))

    def __str__(self):
        return unicode(self).encode('utf-8')

    def __repr__(self):
        return (u"<CallbackEvent id=%s, webhook=%s, event_type='%s'>" %
                (self.id, self.webhook_id, self.event_type))

    def save(self, *args, **kwargs):
        """Update timestamp"""
        self.timestamp = timezone.now()
        super(CallbackEvent, self).save(*args, **kwargs)
        return self

    @property
    def action_data(self):
        """Returns the 'data' node from the payload."""
        return self.event_payload.get('action', {}).get('data')

    @property
    def member(self):
        """Returns 'memberCreator' JSON extracted from event_payload."""
        return self.event_payload.get('action', {}).get('memberCreator')

    @property
    def board(self):
        """Returns 'board' JSON extracted from event_payload."""
        return self.action_data.get('board') if self.action_data else None

    @property
    def list(self):
        """Returns 'list' JSON extracted from event_payload."""
        return self.action_data.get('list') if self.action_data else None

    @property
    def card(self):
        """Returns 'card' JSON extracted from event_payload."""
        return self.action_data.get('card') if self.action_data else None

    @property
    def member_name(self):
        """Return member name if it exists (used in admin)."""
        return self.member.get('fullName') if self.member else None

    @property
    def board_name(self):
        """Return board name if it exists (used in admin)."""
        return self.board.get('name') if self.board else None

    @property
    def list_name(self):
        """Return list name if it exists (used in admin)."""
        return self.list.get('name') if self.list else None

    @property
    def card_name(self):
        """Return card name if it exists (used in admin)."""
        return self.card.get('name') if self.card else None

    @property
    def template(self):
        """Return full path to render template, based on event_type."""
        return 'trello_webhooks/%s.html' % self.event_type

    def render(self):
        """Render the event using an HTML template.

        The path to the template comes from the `template` property,
        which is derived from the event_type.

        If the template does not exist (typically this would be because
        we capture a new event type that we haven't previously encountered),
        a warning is logged, and None is returned. (We return None instead
        of an empty string to make it clear that something has gone wrong -
        an empty string _could_ be a realistic output, if someone has
        overridden a template and spelled the context vars incorrectly.)

        The event_payload is passed in to the template as the context.

        Default templates exist for most event types, but you are encouraged
        to override these templates in your own project (see the template
        property for the full path to the template that is loaded).

        """
        try:
            return render_to_string(self.template, self.event_payload)
        except TemplateDoesNotExist:
            logger.warning(u"Missing or misconfigured template: '%s'",
                           self.template)
            return None
Exemple #3
0
class Entity(DirtyFieldsMixin, models.Model):
    resource = models.ForeignKey(Resource)
    string = models.TextField()
    string_plural = models.TextField(blank=True)
    key = models.TextField(blank=True)  # Needed for webL10n
    comment = models.TextField(blank=True)
    order = models.PositiveIntegerField(default=0)
    source = JSONField(blank=True,
                       default=list)  # List of paths to source code files
    obsolete = models.BooleanField(default=False)

    changed_locales = models.ManyToManyField(
        Locale,
        through='ChangedEntityLocale',
        help_text='List of locales in which translations for this entity have '
        'changed since the last sync.')

    @property
    def marked(self):
        return utils.mark_placeables(self.string)

    @property
    def marked_plural(self):
        return utils.mark_placeables(self.string_plural)

    def __unicode__(self):
        return self.string

    def has_changed(self, locale):
        """
        Check if translations in the given locale have changed since the
        last sync.
        """
        return locale in self.changed_locales.all()

    def mark_changed(self, locale):
        """
        Mark the given locale as having changed translations since the
        last sync.
        """
        ChangedEntityLocale.objects.get_or_create(entity=self, locale=locale)

    def add_translation(self, string, locale, user):
        """
        Add a new translation for this entity. If a matching translation
        already exists, mark it as unfuzzy, and if the given user is a
        translator, approve it.
        """
        try:
            translation = self.translation_set.get(locale=locale,
                                                   string=string)
        except Translation.DoesNotExist:
            translation = Translation(entity=self,
                                      locale=locale,
                                      user=user,
                                      string=string,
                                      fuzzy=True)

        if translation.pk:
            translation.fuzzy = False

        if user.has_perm('base.can_localize') and not translation.approved:
            translation.approved = True
            translation.approved_user = user
            translation.approved_date = timezone.now()

        translation.save()

    def get_translation(self, plural_form=None):
        """Get fetched translation of a given entity."""
        translations = self.fetched_translations

        if plural_form is not None:
            translations = [
                t for t in translations if t.plural_form == plural_form
            ]

        if translations:
            translation = sorted(translations,
                                 key=lambda k: (not k.approved, k.date))[0]
            return {
                'fuzzy': translation.fuzzy,
                'string': translation.string,
                'approved': translation.approved,
                'pk': translation.pk
            }

        else:
            return {
                'fuzzy': False,
                'string': None,
                'approved': False,
                'pk': None
            }

    @classmethod
    def for_project_locale(self, project, locale, paths=None):
        """Get project entities with locale translations."""
        entities = self.objects.filter(resource__project=project,
                                       resource__stats__locale=locale,
                                       obsolete=False)

        if paths:
            try:
                subpage = Subpage.objects.get(project=project, name__in=paths)
                paths = subpage.resources.values_list("path")
            except Subpage.DoesNotExist:
                pass
            entities = entities.filter(resource__path__in=paths) or entities

        entities = entities.prefetch_related(
            'resource',
            Prefetch('translation_set',
                     queryset=Translation.objects.filter(locale=locale),
                     to_attr='fetched_translations'))

        entities_array = []

        for entity in entities:
            translation_array = []

            if entity.string_plural == "":
                translation_array.append(entity.get_translation())

            else:
                for plural_form in range(0, locale.nplurals or 1):
                    translation_array.append(
                        entity.get_translation(plural_form))

            entities_array.append({
                'pk': entity.pk,
                'original': entity.string,
                'marked': entity.marked,
                'original_plural': entity.string_plural,
                'marked_plural': entity.marked_plural,
                'key': entity.key,
                'path': entity.resource.path,
                'format': entity.resource.format,
                'comment': entity.comment,
                'order': entity.order,
                'source': entity.source,
                'obsolete': entity.obsolete,
                'translation': translation_array,
            })

        return sorted(entities_array, key=lambda k: k['order'])
Exemple #4
0
class DiscussionsConfiguration(TimeStampedModel):
    """
    Associates a learning context with discussion provider and configuration
    """

    context_key = LearningContextKeyField(
        primary_key=True,
        db_index=True,
        unique=True,
        max_length=255,
        # Translators: A key specifying a course, library, program,
        # website, or some other collection of content where learning
        # happens.
        verbose_name=_("Learning Context Key"),
    )
    enabled = models.BooleanField(
        default=True,
        help_text=
        _("If disabled, the discussions in the associated learning context/course will be disabled."
          ))
    lti_configuration = models.ForeignKey(
        LtiConfiguration,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        help_text=_("The LTI configuration data for this context/provider."),
    )
    plugin_configuration = JSONField(
        blank=True,
        default={},
        help_text=_(
            "The plugin configuration data for this context/provider."),
    )
    provider_type = models.CharField(
        blank=False,
        max_length=100,
        verbose_name=_("Discussion provider"),
        help_text=_("The discussion tool/provider's id"),
    )
    history = HistoricalRecords()

    def clean(self):
        """
        Validate the model

        Currently, this only support courses, this can be extended
        whenever discussions are available in other contexts
        """
        if not CourseOverview.course_exists(self.context_key):
            raise ValidationError(
                'Context Key should be an existing learning context.')

    def __str__(self):
        return "DiscussionsConfiguration(context_key='{context_key}', provider='{provider}', enabled={enabled})".format(
            context_key=self.context_key,
            provider=self.provider_type,
            enabled=self.enabled,
        )

    def supports(self, feature: str) -> bool:
        """
        Check if the provider supports some feature
        """
        features = AVAILABLE_PROVIDER_MAP.get(
            self.provider_type)['features'] or []
        has_support = bool(feature in features)
        return has_support

    @classmethod
    def is_enabled(cls, context_key: CourseKey) -> bool:
        """
        Check if there is an active configuration for a given course key

        Default to False, if no configuration exists
        """
        configuration = cls.get(context_key)
        return configuration.enabled

    # pylint: disable=undefined-variable
    @classmethod
    def get(cls, context_key: CourseKey) -> cls:
        """
        Lookup a model by context_key
        """
        try:
            configuration = cls.objects.get(context_key=context_key)
        except cls.DoesNotExist:
            configuration = cls(
                context_key=context_key,
                enabled=False,
                provider_type=DEFAULT_PROVIDER_TYPE,
            )
        return configuration

    # pylint: enable=undefined-variable

    @property
    def available_providers(self) -> list[str]:
        return ProviderFilter.current(
            course_key=self.context_key).available_providers

    @classmethod
    def get_available_providers(cls, context_key: CourseKey) -> list[str]:
        return ProviderFilter.current(
            course_key=context_key).available_providers
Exemple #5
0
class Project(models.Model):
    MANUAL = "NA"
    DAILY = "D"
    WEEKLY = "W"
    FORTNIGHT = "F"
    MONTHLY = "M"
    SCHEDULES = [
        (MANUAL, "Manual"),
        (DAILY, "Daily"),
        (WEEKLY, "Weekly"),
        (FORTNIGHT, "Fortnightly"),
        (MONTHLY, "Monthyl"),
    ]

    name = models.CharField(max_length=255)
    type = models.ForeignKey(ProjectType,
                             verbose_name='Type of Project',
                             null=True,
                             blank=True)
    sector = models.ForeignKey(Sector,
                               verbose_name='Sector',
                               null=True,
                               blank=True,
                               related_name='project_sector')
    sub_sector = models.ForeignKey(Sector,
                                   verbose_name='Sub-Sector',
                                   null=True,
                                   blank=True,
                                   related_name='project_sub_sector')
    phone = models.CharField(max_length=255, blank=True, null=True)
    fax = models.CharField(max_length=255, blank=True, null=True)
    email = models.EmailField(blank=True, null=True)
    address = models.TextField(blank=True, null=True)
    website = models.URLField(blank=True, null=True)
    donor = models.CharField(max_length=256, blank=True, null=True)
    public_desc = models.TextField("Public Description", blank=True, null=True)
    additional_desc = models.TextField("Additional Description",
                                       blank=True,
                                       null=True)
    organization = models.ForeignKey(Organization, related_name='projects')
    logo = models.ImageField(upload_to="logo",
                             default="logo/default_project_image.jpg")
    is_active = models.BooleanField(default=True)
    location = PointField(geography=True, srid=4326, blank=True, null=True)
    date_created = models.DateTimeField(auto_now_add=True, blank=True)
    cluster_sites = models.BooleanField(default=False)
    site_meta_attributes = JSONField(default=list)
    site_basic_info = JSONField(default={})
    site_featured_images = JSONField(default=list)
    gsuit_meta = JSONField(default={})
    gsuit_sync = models.CharField(choices=SCHEDULES,
                                  default=MANUAL,
                                  max_length=2)
    gsuit_sync_date = models.DateField(blank=True, null=True)
    gsuit_sync_end_of_month = models.BooleanField(default=False)

    # gsuit_meta sample = {'site_progress':{'link':'', 'last_updated':''}}
    logs = GenericRelation('eventlog.FieldSightLog')
    all_objects = ProjectAllManager()
    objects = ProjectManager()

    geo_layers = models.ManyToManyField('geo.GeoLayer', blank=True)

    class Meta:
        ordering = [
            '-is_active',
            'name',
        ]

    @property
    def latitude(self):
        if self.location:
            return self.location.y

    @property
    def longitude(self):
        if self.location:
            return self.location.x

    def getname(self):
        return self.name

    def __unicode__(self):
        return u'{}'.format(self.name)

    @property
    def get_staffs(self):
        staffs = self.project_roles.filter(
            group__name__in=["Reviewer", "Project Manager"])
        return staffs

    @property
    def get_staffs_both_role(self):
        managers_id = self.project_roles.filter(
            group__name="Project Manager").values_list('user__id', flat=True)
        reviewers_id = self.project_roles.filter(
            group__name="Reviewer").values_list('user__id', flat=True)
        both = list(set(managers_id).intersection(reviewers_id))
        return both

    def get_organization_name(self):
        return self.organization.name

    def get_project_type(self):
        return self.type.name

    @property
    def status(self):
        if self.project_instances.filter(form_status=1).count():
            return 1
        elif self.project_instances.filter(form_status=2).count():
            return 2
        elif self.project_instances.filter(form_status=0).count():
            return 0
        elif self.project_instances.filter(form_status=3).count():
            return 3
        return 4

    def get_project_submission(self):
        instances = self.project_instances.all().order_by('-date')
        outstanding, flagged, approved, rejected = [], [], [], []
        for submission in instances:
            if submission.form_status == 0:
                outstanding.append(submission)
            elif submission.form_status == 1:
                rejected.append(submission)
            elif submission.form_status == 2:
                flagged.append(submission)
            elif submission.form_status == 3:
                approved.append(submission)

        return outstanding, flagged, approved, rejected

    def get_submissions_count(self):
        qs = self.project_instances.aggregate(
            outstanding=Count(
                Case(
                    When(form_status=0, project=self, then=1),
                    output_field=IntegerField(),
                )),
            flagged=Count(
                Case(
                    When(form_status=2, project=self, then=1),
                    output_field=IntegerField(),
                )),
            approved=Count(
                Case(
                    When(form_status=3, project=self, then=1),
                    output_field=IntegerField(),
                )),
            rejected=Count(
                Case(
                    When(form_status=1, project=self, then=1),
                    output_field=IntegerField(),
                )),
        )
        return qs.get('outstanding',
                      0), qs.get('flagged',
                                 0), qs.get('approved',
                                            0), qs.get('rejected', 0)

    def get_absolute_url(self):
        return "/fieldsight/application/#/project-dashboard/{}".format(self.pk)
Exemple #6
0
class Shop(ChangeProtected, TranslatableShuupModel):
    protected_fields = ["currency", "prices_include_tax"]
    change_protect_message = _(
        "The following fields cannot be changed since there are existing orders for this shop"
    )

    created_on = models.DateTimeField(auto_now_add=True,
                                      editable=False,
                                      verbose_name=_('created on'))
    modified_on = models.DateTimeField(auto_now=True,
                                       editable=False,
                                       db_index=True,
                                       verbose_name=_('modified on'))
    identifier = InternalIdentifierField(unique=True, max_length=128)
    domain = models.CharField(
        max_length=128,
        blank=True,
        null=True,
        unique=True,
        verbose_name=_("domain"),
        help_text=
        _("Your shop domain name. Use this field to configure the URL that is used to visit your site. "
          "Note: this requires additional configuration through your internet domain registrar."
          ))
    status = EnumIntegerField(
        ShopStatus,
        default=ShopStatus.DISABLED,
        verbose_name=_("status"),
        help_text=_(
            "Your shop status. Disable your shop if it is no longer in use."))
    owner = models.ForeignKey("Contact",
                              blank=True,
                              null=True,
                              on_delete=models.SET_NULL,
                              verbose_name=_("contact"))
    options = JSONField(blank=True, null=True, verbose_name=_("options"))
    currency = CurrencyField(
        default=_get_default_currency,
        verbose_name=_("currency"),
        help_text=
        _("The primary shop currency. This is the currency used when selling your products."
          ))
    prices_include_tax = models.BooleanField(
        default=True,
        verbose_name=_("prices include tax"),
        help_text=
        _("This option defines whether product prices entered in admin include taxes. "
          "Note this behavior can be overridden with contact group pricing."))
    logo = FilerImageField(verbose_name=_("logo"),
                           blank=True,
                           null=True,
                           on_delete=models.SET_NULL,
                           help_text=_("Shop logo. Will be shown at theme."),
                           related_name="shop_logos")

    favicon = FilerImageField(
        verbose_name=_("favicon"),
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        help_text=_(
            "Shop favicon. Will be shown next to the address on browser."),
        related_name="shop_favicons")

    maintenance_mode = models.BooleanField(
        verbose_name=_("maintenance mode"),
        default=False,
        help_text=
        _("Check this if you would like to make your shop temporarily unavailable while you do some shop maintenance."
          ))
    contact_address = models.ForeignKey("MutableAddress",
                                        verbose_name=_("contact address"),
                                        blank=True,
                                        null=True,
                                        on_delete=models.SET_NULL)
    staff_members = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                           blank=True,
                                           related_name="+",
                                           verbose_name=_('staff members'))
    labels = models.ManyToManyField("Label",
                                    blank=True,
                                    related_name="shops",
                                    verbose_name=_("labels"))

    translations = TranslatedFields(
        name=models.CharField(
            max_length=64,
            verbose_name=_("name"),
            help_text=_(
                "The shop name. This name is displayed throughout admin.")),
        public_name=models.CharField(
            max_length=64,
            verbose_name=_("public name"),
            help_text=
            _("The public shop name. This name is displayed in the store front and in any customer email correspondence."
              )),
        description=models.TextField(
            blank=True,
            verbose_name=_('description'),
            help_text=_(
                "To make your shop stand out, give it an awesome description. "
                "This is what will help your shoppers learn about your shop. "
                "It will also help shoppers find your store from the web.")),
        short_description=models.CharField(
            max_length=150,
            blank=True,
            verbose_name=_('short description'),
            help_text=
            _("Enter a short description for your shop. "
              "The short description will be used to get the attention of your "
              "customer with a small but precise description of your shop.")),
        maintenance_message=models.CharField(
            max_length=300,
            blank=True,
            verbose_name=_("maintenance message"),
            help_text=
            _("The message to display to customers while your shop is in maintenance mode."
              )))

    objects = ShopManager()

    class Meta:
        verbose_name = _('shop')
        verbose_name_plural = _('shops')

    def __str__(self):
        return self.safe_translation_getter("name",
                                            default="Shop %d" % self.pk)

    def create_price(self, value):
        """
        Create a price with given value and settings of this shop.

        Takes the ``prices_include_tax`` and ``currency`` settings of
        this Shop into account.

        :type value: decimal.Decimal|int|str
        :rtype: shuup.core.pricing.Price
        """
        if self.prices_include_tax:
            return TaxfulPrice(value, self.currency)
        else:
            return TaxlessPrice(value, self.currency)

    def _are_changes_protected(self):
        return Order.objects.filter(shop=self).exists()
Exemple #7
0
class JSONFieldWithDefaultTestModel(models.Model):
    json = JSONField(default={"sukasuka": "YAAAAAZ"})

    class Meta:
        app_label = 'jsonfield'
Exemple #8
0
class EmailKind(models.Model):
    """
    This model represents a kind of email. Created and configured manually
    through the admin panel.
    """
    MIN_NAME_LENGTH = 6

    name = models.CharField(max_length=255, verbose_name='email kind name')
    language = models.CharField(max_length=2, choices=settings.LANGUAGES,
                                default=settings.DEFAULT_LANGUAGE_CODE,
                                verbose_name='language')
    description = models.CharField(max_length=300, verbose_name='description',
                                   default='')
    template = models.TextField(verbose_name='template')
    plain_template = models.TextField(verbose_name='plain text template')
    default_context = JSONField(
        load_kwargs={'object_pairs_hook': collections.OrderedDict},
        blank=True, verbose_name='default template context', default={}
    )
    default_sender = models.CharField(
        max_length=255,
        blank=True,
        verbose_name='default from (can be named format)'
    )
    default_recipients = models.TextField(
        blank=True,
        verbose_name='default to (comma separated, can be named format)'
    )
    default_subject = models.TextField(blank=True,
                                       verbose_name='default subject')
    default_reply_to = models.TextField(
        blank=True,
        verbose_name='default reply to (comma separated, can be named format)'
    )
    active = models.BooleanField(default=True, verbose_name='Is active')
    fragments = models.ManyToManyField(
        'EmailKindFragment',
        related_name='kinds',
        blank=True
    )

    model_name = 'EmailKind'

    class Meta:
        unique_together = ('name', 'language')
        ordering = ['name', 'language']

    def __str__(self):
        return '{}/{}'.format(self.name, self.language)

    def iter_all_images(self):
        for img in self.images.all():
            yield img
        for fragment in self.fragments.all():
            for img in fragment.images.all():
                yield img

    def generate_entry(self, params):
        """
        Creates and persists an EmailEntry given the current EmailKind.
        Checks that both the EmailKind and the params for EmailEntry are
        consistent.

        @type params **dict
        @param params Dictionary containing data to build the EmailEntry from
            the current EmailKind. Example:
            {
                'sender': '*****@*****.**',
                'recipients': ['*****@*****.**', '*****@*****.**', ...],
                'reply_to': ['*****@*****.**', '*****@*****.**', ...],
                'customer_id': '3838383',
                'subject': 'This is the email subject',
                'context': {'first_name': 'Troll', ...},
                'send_at': 1434029573, # Unix timestamp, UTC
                'check_url': 'http://myservice.qdqmedia.com/canisend/4983/323'
                'attachs': [{
                                'filename': 'invoice.pdf',
                                'content_type': 'application/pdf',
                                'content': 'raw content of the file'
                            }, ...]
            }
        """
        try:
            self.assert_well_formed()
            self.assert_sane_params(params)
        except EmailAssertionError as e:
            logger.exception("The EmailKind: {ek} is not well formed or was badly called with: {pa}"\
                .format(ek=self, pa=params))
            raise e

        context = params.get('context', None) or self.default_context
        sender = params.get('sender', None) or self.default_sender
        recipients = ','.join(params.get('recipients', [])) or \
                     self.default_recipients
        subject = params.get('subject', None) or self.default_subject
        reply_to = ','.join(params.get('reply_to', [])) or \
                   self.default_reply_to


        with transaction.atomic():
            entry = EmailEntry.objects.create(
                kind=self,
                customer_id=params.get('customer_id', ''),
                context=context,
                sender=sender,
                recipients=recipients,
                subject=subject,
                reply_to=reply_to,
                send_at=params.get('send_at', None),
                check_url=params.get('check_url', ''),
                backend=params.get('backend', ''),
                metadata=params.get('meta_fields', {}),
            )

            attachs = params.get('attachs', [])
            for attach in attachs:
                uploaded_file = SimpleUploadedFile(
                    attach['filename'],
                    give_me_bytes(base64.b64decode(attach['content'])),
                    attach['content_type']
                )
                Attachment.objects.create(entry=entry, attached_file=uploaded_file,
                                          content_type=attach['content_type'],
                                          name=attach['filename'])

        return entry

    def assert_well_formed(self):
        cassert(len(self.name) >= self.MIN_NAME_LENGTH,
                'kind: {}. too short name'.format(self))
        # TODO: Do something here when language policy is clear.
        # assert self.language in allowed_language_codes(), \
        #     'kind: {}. language not recognized'.format(self)
        cassert(len(self.template) > 0,
                'kind: {}. empty template'.format(self))
        cassert(len(self.plain_template) > 0,
                'kind: {}. empty plain template'.format(self))

    def assert_sane_params(self, params):
        cassert(self.default_sender or ('sender' in params and params['sender']),
                'kind: {}. sender never specified'.format(self))
        cassert(self.default_recipients or \
                ('recipients' in params and params['recipients'] and \
                 any(r for r in params['recipients'])),
                'kind: {}. recipients never specified'.format(self))
        cassert(self.default_subject or \
                ('subject' in params and params['subject']),
                'kind: {}. subject never specified'.format(self))

        if 'backend' in params:
            flattened_backends = sum(settings.CUSTOM_EMAIL_BACKENDS, ())
            cassert(
                params['backend'] in flattened_backends,
                'kind: {}. backend {} not known'.format(self, params['backend'])
            )
Exemple #9
0
class Cart(models.Model):
    """A shopping cart."""

    status = models.CharField(
        max_length=32, choices=CartStatus.CHOICES, default=CartStatus.OPEN)
    created = models.DateTimeField(auto_now_add=True)
    last_status_change = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, blank=True, null=True, related_name='carts',
        on_delete=models.CASCADE)
    email = models.EmailField(blank=True, null=True)
    token = models.UUIDField(primary_key=True, default=uuid4, editable=False)
    voucher = models.ForeignKey(
        'discount.Voucher', null=True, related_name='+',
        on_delete=models.SET_NULL)
    checkout_data = JSONField(null=True, editable=False)
    total = PriceField(
        currency=settings.DEFAULT_CURRENCY, max_digits=12, decimal_places=2,
        default=0)
    quantity = models.PositiveIntegerField(default=0)

    objects = CartQueryset.as_manager()

    class Meta:
        ordering = ('-last_status_change',)

    def __init__(self, *args, **kwargs):
        self.discounts = kwargs.pop('discounts', None)
        super().__init__(*args, **kwargs)

    def update_quantity(self):
        """Recalculate cart quantity based on lines."""
        total_lines = self.count()['total_quantity']
        if not total_lines:
            total_lines = 0
        self.quantity = total_lines
        self.save(update_fields=['quantity'])

    def change_status(self, status):
        """Change cart status."""
        # FIXME: investigate replacing with django-fsm transitions
        if status not in dict(CartStatus.CHOICES):
            raise ValueError('Not expected status')
        if status != self.status:
            self.status = status
            self.last_status_change = now()
            self.save()

    def change_user(self, user):
        """Assign cart to a user.

        If the user already has an open cart assigned, cancel it.
        """
        open_cart = find_open_cart_for_user(user)
        if open_cart is not None:
            open_cart.change_status(status=CartStatus.CANCELED)
        self.user = user
        self.save(update_fields=['user'])

    def is_shipping_required(self):
        """Return `True` if any of the lines requires shipping."""
        return any(line.is_shipping_required() for line in self.lines.all())

    def __repr__(self):
        return 'Cart(quantity=%s)' % (self.quantity,)

    def __len__(self):
        return self.lines.count()

    # pylint: disable=R0201
    def get_subtotal(self, item, **kwargs):
        """Return the cost of a cart line."""
        return item.get_total(**kwargs)

    def get_total(self, **kwargs):
        """Return the total cost of the cart prior to shipping."""
        subtotals = [
            self.get_subtotal(item, **kwargs) for item in self.lines.all()]
        if not subtotals:
            raise AttributeError('Calling get_total() on an empty item set')
        zero = Price(0, currency=settings.DEFAULT_CURRENCY)
        return sum(subtotals, zero)

    def count(self):
        """Return the total quantity in cart."""
        lines = self.lines.all()
        return lines.aggregate(total_quantity=models.Sum('quantity'))

    def clear(self):
        """Remove the cart."""
        self.delete()

    def create_line(self, variant, quantity, data):
        """Create a cart line for given variant, quantity and optional data.

        The `data` parameter may be used to differentiate between items with
        different customization options.
        """
        return self.lines.create(
            variant=variant, quantity=quantity, data=data or {})

    def get_line(self, variant, data=None):
        """Return a line matching the given variant and data if any."""
        all_lines = self.lines.all()
        if data is None:
            data = {}
        line = [line for line in all_lines
                if line.variant_id == variant.id and line.data == data]
        if line:
            return line[0]

    def add(self, variant, quantity=1, data=None, replace=False,
            check_quantity=True):
        """Add a product vartiant to cart.

        The `data` parameter may be used to differentiate between items with
        different customization options.

        If `replace` is truthy then any previous quantity is discarded instead
        of added to.
        """
        cart_line, dummy_created = self.lines.get_or_create(
            variant=variant, defaults={'quantity': 0, 'data': data or {}})
        if replace:
            new_quantity = quantity
        else:
            new_quantity = cart_line.quantity + quantity

        if new_quantity < 0:
            raise ValueError('%r is not a valid quantity (results in %r)' % (
                quantity, new_quantity))

        if check_quantity:
            variant.check_quantity(new_quantity)

        cart_line.quantity = new_quantity

        if not cart_line.quantity:
            cart_line.delete()
        else:
            cart_line.save(update_fields=['quantity'])
        self.update_quantity()

    def partition(self):
        """Split the card into a list of groups for shipping."""
        grouper = (
            lambda p: 'physical' if p.is_shipping_required() else 'digital')
        return partition(self.lines.all(), grouper, ProductGroup)
Exemple #10
0
class Build(models.Model):
    """Build data."""

    project = models.ForeignKey(
        Project,
        verbose_name=_('Project'),
        related_name='builds',
    )
    version = models.ForeignKey(
        Version,
        verbose_name=_('Version'),
        null=True,
        related_name='builds',
    )
    type = models.CharField(
        _('Type'),
        max_length=55,
        choices=BUILD_TYPES,
        default='html',
    )
    state = models.CharField(
        _('State'),
        max_length=55,
        choices=BUILD_STATE,
        default='finished',
    )
    date = models.DateTimeField(_('Date'), auto_now_add=True)
    success = models.BooleanField(_('Success'), default=True)

    setup = models.TextField(_('Setup'), null=True, blank=True)
    setup_error = models.TextField(_('Setup error'), null=True, blank=True)
    output = models.TextField(_('Output'), default='', blank=True)
    error = models.TextField(_('Error'), default='', blank=True)
    exit_code = models.IntegerField(_('Exit code'), null=True, blank=True)
    commit = models.CharField(
        _('Commit'),
        max_length=255,
        null=True,
        blank=True,
    )
    _config = JSONField(_('Configuration used in the build'), default=dict)

    length = models.IntegerField(_('Build Length'), null=True, blank=True)

    builder = models.CharField(
        _('Builder'),
        max_length=255,
        null=True,
        blank=True,
    )

    cold_storage = models.NullBooleanField(
        _('Cold Storage'),
        help_text='Build steps stored outside the database.',
    )

    # Manager

    objects = BuildQuerySet.as_manager()

    CONFIG_KEY = '__config'

    class Meta:
        ordering = ['-date']
        get_latest_by = 'date'
        index_together = [['version', 'state', 'type']]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._config_changed = False

    @property
    def previous(self):
        """
        Returns the previous build to the current one.

        Matching the project and version.
        """
        date = self.date or timezone.now()
        if self.project is not None and self.version is not None:
            return (Build.objects.filter(
                project=self.project,
                version=self.version,
                date__lt=date,
            ).order_by('-date').first())
        return None

    @property
    def config(self):
        """
        Get the config used for this build.

        Since we are saving the config into the JSON field only when it differs
        from the previous one, this helper returns the correct JSON used in this
        Build object (it could be stored in this object or one of the previous
        ones).
        """
        if self.CONFIG_KEY in self._config:
            return Build.objects.get(pk=self._config[self.CONFIG_KEY])._config
        return self._config

    @config.setter
    def config(self, value):
        """
        Set `_config` to value.

        `_config` should never be set directly from outside the class.
        """
        self._config = value
        self._config_changed = True

    def save(self, *args, **kwargs):  # noqa
        """
        Save object.

        To save space on the db we only save the config if it's different
        from the previous one.

        If the config is the same, we save the pk of the object
        that has the **real** config under the `CONFIG_KEY` key.
        """
        if self.pk is None or self._config_changed:
            previous = self.previous
            # yapf: disable
            if (
                previous is not None and self._config and
                self._config == previous.config
            ):
                # yapf: enable
                previous_pk = previous._config.get(self.CONFIG_KEY,
                                                   previous.pk)
                self._config = {self.CONFIG_KEY: previous_pk}
        super().save(*args, **kwargs)
        self._config_changed = False

    def __str__(self):
        return ugettext(
            'Build {project} for {usernames} ({pk})'.format(
                project=self.project,
                usernames=' '.join(
                    self.project.users.all().values_list('username',
                                                         flat=True), ),
                pk=self.pk,
            ), )

    def get_absolute_url(self):
        return reverse('builds_detail', args=[self.project.slug, self.pk])

    @property
    def finished(self):
        """Return if build has a finished state."""
        return self.state == BUILD_STATE_FINISHED

    @property
    def is_stale(self):
        """Return if build state is triggered & date more than 5m ago."""
        mins_ago = timezone.now() - datetime.timedelta(minutes=5)
        return self.state == BUILD_STATE_TRIGGERED and self.date < mins_ago
Exemple #11
0
class ProjectDebugFile(Model):
    __core__ = False

    file = FlexibleForeignKey('sentry.File')
    object_name = models.TextField()
    cpu_name = models.CharField(max_length=40)
    project = FlexibleForeignKey('sentry.Project', null=True)
    debug_id = models.CharField(max_length=64, db_column='uuid')
    data = JSONField(null=True)
    objects = ProjectDebugFileManager()

    class Meta:
        index_together = (('project', 'debug_id'), )
        db_table = 'sentry_projectdsymfile'
        app_label = 'sentry'

    __repr__ = sane_repr('object_name', 'cpu_name', 'debug_id')

    @property
    def dif_type(self):
        ct = self.file.headers.get('Content-Type', 'unknown').lower()
        return KNOWN_DIF_TYPES.get(ct, 'unknown')

    @property
    def file_extension(self):
        if self.dif_type == 'breakpad':
            return '.sym'
        if self.dif_type == 'macho':
            return '.dSYM'
        if self.dif_type == 'proguard':
            return '.txt'
        if self.dif_type == 'elf':
            return '.debug'

        return ''

    @property
    def supports_caches(self):
        return ProjectSymCacheFile.computes_from(self) \
            or ProjectCfiCacheFile.computes_from(self)

    @property
    def features(self):
        return frozenset((self.data or {}).get('features', []))

    def delete(self, *args, **kwargs):
        dif_id = self.id

        with mysql_disabled_integrity(db=ProjectDebugFile.objects.db):
            with transaction.atomic():
                # First, delete the debug file entity. This ensures no other
                # worker can attach caches to it. Integrity checks are deferred
                # within this transaction, so existing caches stay intact.
                super(ProjectDebugFile, self).delete(*args, **kwargs)

                # Explicitly select referencing caches and delete them. Using
                # the backref does not work, since `dif.id` is None after the
                # delete.
                symcaches = ProjectSymCacheFile.objects \
                    .filter(debug_file_id=dif_id) \
                    .select_related('cache_file')
                for symcache in symcaches:
                    symcache.delete()

                cficaches = ProjectCfiCacheFile.objects \
                    .filter(debug_file_id=dif_id) \
                    .select_related('cache_file')
                for cficache in cficaches:
                    cficache.delete()

        self.file.delete()
Exemple #12
0
class Config(UuidAuditedModel):
    """
    Set of configuration values applied as environment variables
    during runtime execution of the Application.
    """

    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    app = models.ForeignKey('App', on_delete=models.CASCADE)
    values = JSONField(default={}, blank=True)
    memory = JSONField(default={}, blank=True)
    cpu = JSONField(default={}, blank=True)
    tags = JSONField(default={}, blank=True)
    registry = JSONField(default={}, blank=True)
    healthcheck = JSONField(default={}, blank=True)

    class Meta:
        get_latest_by = 'created'
        ordering = ['-created']
        unique_together = (('app', 'uuid'),)

    def __str__(self):
        return "{}-{}".format(self.app.id, str(self.uuid)[:7])

    def _migrate_legacy_healthcheck(self):
        """
        Get all healthchecks options together for use in scheduler
        """
        # return if no legacy healthcheck is found
        if 'HEALTHCHECK_URL' not in self.values.keys():
            return

        path = self.values.get('HEALTHCHECK_URL', '/')
        timeout = int(self.values.get('HEALTHCHECK_TIMEOUT', 50))
        delay = int(self.values.get('HEALTHCHECK_INITIAL_DELAY', 50))
        period_seconds = int(self.values.get('HEALTHCHECK_PERIOD_SECONDS', 10))
        success_threshold = int(self.values.get('HEALTHCHECK_SUCCESS_THRESHOLD', 1))
        failure_threshold = int(self.values.get('HEALTHCHECK_FAILURE_THRESHOLD', 3))

        self.healthcheck['livenessProbe'] = {
            'initialDelaySeconds': delay,
            'timeoutSeconds': timeout,
            'periodSeconds': period_seconds,
            'successThreshold': success_threshold,
            'failureThreshold': failure_threshold,
            'httpGet': {
                'path': path,
            }
        }

    def set_registry(self):
        # lower case all registry options for consistency
        self.registry = {key.lower(): value for key, value in self.registry.copy().items()}

        # PORT must be set if private registry is being used
        if self.registry and self.values.get('PORT', None) is None:
            # only thing that can get past post_save in the views
            raise DeisException(
                'PORT needs to be set in the config '
                'when using a private registry')

    def set_tags(self, previous_config):
        """verify the tags exist on any nodes as labels"""
        if not self.tags:
            return

        # Get all nodes with label selectors
        nodes = self._scheduler.get_nodes(labels=self.tags).json()
        if nodes['items']:
            return

        labels = ['{}={}'.format(key, value) for key, value in self.tags.items()]
        message = 'No nodes matched the provided labels: {}'.format(', '.join(labels))

        # Find out if there are any other tags around
        old_tags = getattr(previous_config, 'tags')
        if old_tags:
            old = ['{}={}'.format(key, value) for key, value in old_tags.items()]
            new = set(labels) - set(old)
            if new:
                message += ' - Addition of {} is the cause'.format(', '.join(new))

        raise DeisException(message)

    def save(self, **kwargs):
        """merge the old config with the new"""
        try:
            # Get config from the latest available release
            try:
                previous_config = self.app.release_set.latest().config
            except Release.DoesNotExist:
                # If that doesn't exist then fallback on app config
                # usually means a totally new app
                previous_config = self.app.config_set.latest()

            for attr in ['cpu', 'memory', 'tags', 'registry', 'values', 'healthcheck']:
                data = getattr(previous_config, attr, {}).copy()
                new_data = getattr(self, attr, {}).copy()

                # remove config keys if a null value is provided
                for key, value in new_data.items():
                    if value is None:
                        # error if unsetting non-existing key
                        if key not in data:
                            raise UnprocessableEntity('{} does not exist under {}'.format(key, attr))  # noqa
                        data.pop(key)
                    else:
                        data[key] = value
                setattr(self, attr, data)

            self._migrate_legacy_healthcheck()
            self.set_registry()
            self.set_tags(previous_config)
        except Config.DoesNotExist:
            pass

        return super(Config, self).save(**kwargs)
Exemple #13
0
class AuthToken(BaseSubUserModel):
    id: int = models.BigAutoField(primary_key=True, editable=False)
    usercomponent = models.ForeignKey("spider_base.UserComponent",
                                      on_delete=models.CASCADE,
                                      related_name="authtokens")
    attached_to_content = models.ForeignKey("spider_base.AssignedContent",
                                            on_delete=models.CASCADE,
                                            related_name="attached_tokens",
                                            null=True,
                                            blank=True)
    # -1=false,0=usercomponent,1-...=anchor
    persist: int = models.BigIntegerField(blank=True,
                                          default=-1,
                                          db_index=True)
    # brute force protection
    #  16 = id in hexadecimal
    #  +2 for seperators
    # when swapping tokens the id in the token can missmatch
    #  so don't rely on it
    token: str = models.CharField(max_length=MAX_TOKEN_B64_SIZE +
                                  hex_size_of_bigid + 2,
                                  db_index=True,
                                  unique=True,
                                  null=True,
                                  validators=[validator_token])
    referrer = models.ForeignKey("spider_base.ReferrerObject",
                                 on_delete=models.CASCADE,
                                 related_name="tokens",
                                 blank=True,
                                 null=True)
    session_key: str = models.CharField(max_length=40, null=True)
    extra: dict = JSONField(default=dict, blank=True)
    created = models.DateTimeField(auto_now_add=True, editable=False)
    default_update_fields = None

    objects = AuthTokenManager()

    def __str__(self):
        return "{}...".format(self.token[:-_striptoken])

    def initialize_token(self):
        self.token = create_b64_id_token(self.id, "_",
                                         getattr(settings, "TOKEN_SIZE", 30))

    def save(self, **kwargs):
        # maybe a little overengineered for that a clash can only happen
        # if an old generated token has the same token as a new one
        # the used id switched from the one of a usercomponent to the one
        # of the token
        start_token_creation = not self.token
        created = not self.id
        if start_token_creation:
            if not created:
                update_fields = set(
                    kwargs.pop("update_fields", self.default_update_fields))
                update_fields.discard("token")
                kwargs["update_fields"] = update_fields
            super().save(**kwargs)
            for i in range(0, 1000):
                if i >= 999:
                    # in reality this path will be very unlikely
                    if created:
                        self.delete()
                        self.token = None
                    raise TokenCreationError(
                        'A possible infinite loop was detected')
                self.initialize_token()
                try:
                    with transaction.atomic():
                        super().save(update_fields=["token"],
                                     using=kwargs.get("using"))
                    break
                except IntegrityError:
                    pass
        else:
            super().save(**kwargs)
Exemple #14
0
class Feed(models.Model):
    """Represents a single GTFS feed.

    This data is not part of the General Transit Feed Specification.  It is
    used to allow storage of several GTFS feeds in the same database.
    """
    name = models.CharField(max_length=255)
    created = models.DateTimeField(auto_now_add=True)
    meta = JSONField(default={}, blank=True, null=True)

    class Meta:
        db_table = 'gtfs_feed'
        app_label = 'multigtfs'

    def __str__(self):
        if self.name:
            return "%d %s" % (self.id, self.name)
        else:
            return "%d" % self.id

    def import_gtfs(self, gtfs_obj):
        """Import a GTFS file as feed

        Keyword arguments:
        gtfs_obj - A path to a zipped GTFS file, a path to an extracted
            GTFS file, or an open GTFS zip file.

        Returns is a list of objects imported
        """
        total_start = time.time()

        # Determine the type of gtfs_obj
        opener = None
        filelist = None
        if isinstance(gtfs_obj, string_types) and os.path.isdir(gtfs_obj):
            opener = open
            filelist = []
            for dirpath, dirnames, filenames in os.walk(gtfs_obj):
                filelist.extend([os.path.join(dirpath, f) for f in filenames])
        else:
            zfile = ZipFile(gtfs_obj, 'r')
            opener = opener_from_zipfile(zfile)
            filelist = zfile.namelist()

        gtfs_order = (
            Agency, Stop, Route, Service, ServiceDate, Trip,
            StopTime, Frequency, Fare, FareRule, Transfer, FeedInfo,
        )
        post_save.disconnect(dispatch_uid='post_save_stop')
        try:
            for klass in gtfs_order:
                for f in filelist:
                    if f.endswith(klass._filename):
                        start_time = time.  time()
                        table = opener(f)
                        count = klass.import_txt(table, self) or 0
                        end_time = time.time()
                        logger.info(
                            'Imported %s (%d %s) in %0.1f seconds',
                            klass._filename, count,
                            klass._meta.verbose_name_plural,
                            end_time - start_time)
                        table.close()

        finally:
            post_save.connect(post_save_stop, sender=Stop)

    def export_gtfs(self, gtfs_file):
        """Export a GTFS file as feed

        Keyword arguments:
        gtfs_file - A path or file-like object for the GTFS feed

        This function will close the file in order to finalize it.
        """
        total_start = time.time()
        z = open_writable_zipfile(gtfs_file)

        gtfs_order = (
            Agency, Service, ServiceDate, Fare, FareRule, FeedInfo, Frequency,
            Route, StopTime, Stop, Transfer, Trip,
        )

        for klass in gtfs_order:
            start_time = time.time()
            content = klass.export_txt(self)
            if content:
                z.writestr(klass._filename, content)
                end_time = time.time()
                record_count = content.count(type(content)('\n')) - 1
                logger.info(
                    'Exported %s (%d %s) in %0.1f seconds',
                    klass._filename, record_count,
                    klass._meta.verbose_name_plural,
                    end_time - start_time)
        z.close()
        total_end = time.time()
        logger.info(
            'Export completed in %0.1f seconds.', total_end - total_start)
Exemple #15
0
class Build(models.Model):
    """Build data."""

    project = models.ForeignKey(
        Project,
        verbose_name=_('Project'),
        related_name='builds',
        on_delete=models.CASCADE,
    )
    version = models.ForeignKey(
        Version,
        verbose_name=_('Version'),
        null=True,
        related_name='builds',
        on_delete=models.CASCADE,
    )
    type = models.CharField(
        _('Type'),
        max_length=55,
        choices=BUILD_TYPES,
        default='html',
    )
    state = models.CharField(
        _('State'),
        max_length=55,
        choices=BUILD_STATE,
        default='finished',
    )
    date = models.DateTimeField(_('Date'), auto_now_add=True)
    success = models.BooleanField(_('Success'), default=True)

    setup = models.TextField(_('Setup'), null=True, blank=True)
    setup_error = models.TextField(_('Setup error'), null=True, blank=True)
    output = models.TextField(_('Output'), default='', blank=True)
    error = models.TextField(_('Error'), default='', blank=True)
    exit_code = models.IntegerField(_('Exit code'), null=True, blank=True)
    commit = models.CharField(
        _('Commit'),
        max_length=255,
        null=True,
        blank=True,
    )
    _config = JSONField(_('Configuration used in the build'), default=dict)

    length = models.IntegerField(_('Build Length'), null=True, blank=True)

    builder = models.CharField(
        _('Builder'),
        max_length=255,
        null=True,
        blank=True,
    )

    cold_storage = models.NullBooleanField(
        _('Cold Storage'),
        help_text='Build steps stored outside the database.',
    )

    # Managers
    objects = BuildManager.from_queryset(BuildQuerySet)()
    # Only include BRANCH, TAG, UNKNOWN type Version builds.
    internal = InternalBuildManager.from_queryset(BuildQuerySet)()
    # Only include EXTERNAL type Version builds.
    external = ExternalBuildManager.from_queryset(BuildQuerySet)()

    CONFIG_KEY = '__config'

    class Meta:
        ordering = ['-date']
        get_latest_by = 'date'
        index_together = [['version', 'state', 'type']]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._config_changed = False

    @property
    def previous(self):
        """
        Returns the previous build to the current one.

        Matching the project and version.
        """
        date = self.date or timezone.now()
        if self.project is not None and self.version is not None:
            return (Build.objects.filter(
                project=self.project,
                version=self.version,
                date__lt=date,
            ).order_by('-date').first())
        return None

    @property
    def config(self):
        """
        Get the config used for this build.

        Since we are saving the config into the JSON field only when it differs
        from the previous one, this helper returns the correct JSON used in this
        Build object (it could be stored in this object or one of the previous
        ones).
        """
        if self.CONFIG_KEY in self._config:
            return (Build.objects.only('_config').get(
                pk=self._config[self.CONFIG_KEY])._config)
        return self._config

    @config.setter
    def config(self, value):
        """
        Set `_config` to value.

        `_config` should never be set directly from outside the class.
        """
        self._config = value
        self._config_changed = True

    def save(self, *args, **kwargs):  # noqa
        """
        Save object.

        To save space on the db we only save the config if it's different
        from the previous one.

        If the config is the same, we save the pk of the object
        that has the **real** config under the `CONFIG_KEY` key.
        """
        if self.pk is None or self._config_changed:
            previous = self.previous
            # yapf: disable
            if (
                previous is not None and self._config and
                self._config == previous.config
            ):
                # yapf: enable
                previous_pk = previous._config.get(self.CONFIG_KEY,
                                                   previous.pk)
                self._config = {self.CONFIG_KEY: previous_pk}
        super().save(*args, **kwargs)
        self._config_changed = False

    def __str__(self):
        return ugettext(
            'Build {project} for {usernames} ({pk})'.format(
                project=self.project,
                usernames=' '.join(
                    self.project.users.all().values_list('username',
                                                         flat=True), ),
                pk=self.pk,
            ), )

    def get_absolute_url(self):
        return reverse('builds_detail', args=[self.project.slug, self.pk])

    def get_full_url(self):
        """
        Get full url of the build including domain.

        Example: https://readthedocs.org/projects/pip/builds/99999999/
        """
        scheme = 'http' if settings.DEBUG else 'https'
        full_url = '{scheme}://{domain}{absolute_url}'.format(
            scheme=scheme,
            domain=settings.PRODUCTION_DOMAIN,
            absolute_url=self.get_absolute_url())
        return full_url

    def get_commit_url(self):
        """Return the commit URL."""
        repo_url = self.project.repo
        if self.is_external:
            if 'github' in repo_url:
                user, repo = get_github_username_repo(repo_url)
                if not user and not repo:
                    return ''

                repo = repo.rstrip('/')
                return GITHUB_PULL_REQUEST_COMMIT_URL.format(
                    user=user,
                    repo=repo,
                    number=self.version.verbose_name,
                    commit=self.commit)
            if 'gitlab' in repo_url:
                user, repo = get_gitlab_username_repo(repo_url)
                if not user and not repo:
                    return ''

                repo = repo.rstrip('/')
                return GITLAB_MERGE_REQUEST_COMMIT_URL.format(
                    user=user,
                    repo=repo,
                    number=self.version.verbose_name,
                    commit=self.commit)
            # TODO: Add External Version Commit URL for BitBucket.
        else:
            if 'github' in repo_url:
                user, repo = get_github_username_repo(repo_url)
                if not user and not repo:
                    return ''

                repo = repo.rstrip('/')
                return GITHUB_COMMIT_URL.format(user=user,
                                                repo=repo,
                                                commit=self.commit)
            if 'gitlab' in repo_url:
                user, repo = get_gitlab_username_repo(repo_url)
                if not user and not repo:
                    return ''

                repo = repo.rstrip('/')
                return GITLAB_COMMIT_URL.format(user=user,
                                                repo=repo,
                                                commit=self.commit)
            if 'bitbucket' in repo_url:
                user, repo = get_bitbucket_username_repo(repo_url)
                if not user and not repo:
                    return ''

                repo = repo.rstrip('/')
                return BITBUCKET_COMMIT_URL.format(user=user,
                                                   repo=repo,
                                                   commit=self.commit)

        return None

    @property
    def finished(self):
        """Return if build has a finished state."""
        return self.state == BUILD_STATE_FINISHED

    @property
    def is_stale(self):
        """Return if build state is triggered & date more than 5m ago."""
        mins_ago = timezone.now() - datetime.timedelta(minutes=5)
        return self.state == BUILD_STATE_TRIGGERED and self.date < mins_ago

    @property
    def is_external(self):
        return self.version.type == EXTERNAL

    @property
    def external_version_name(self):
        if self.is_external:
            if self.project.git_provider_name == GITHUB_BRAND:
                return GITHUB_EXTERNAL_VERSION_NAME

            if self.project.git_provider_name == GITLAB_BRAND:
                return GITLAB_EXTERNAL_VERSION_NAME

            # TODO: Add External Version Name for BitBucket.
            return GENERIC_EXTERNAL_VERSION_NAME
        return None

    def using_latest_config(self):
        return int(self.config.get('version',
                                   '1')) == LATEST_CONFIGURATION_VERSION
Exemple #16
0
class StopTime(Base):
    """A specific stop on a route on a trip.

    This implements stop_times.txt in the GTFS feed
    """
    trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
    stop = models.ForeignKey(Stop, on_delete=models.CASCADE)
    arrival_time = SecondsField(
        default=None,
        null=True,
        blank=True,
        help_text="Arrival time. Must be set for end stops of trip.")
    departure_time = SecondsField(
        default=None,
        null=True,
        blank=True,
        help_text='Departure time. Must be set for end stops of trip.')
    stop_sequence = models.IntegerField()
    stop_headsign = models.CharField(
        max_length=255,
        blank=True,
        help_text="Sign text that identifies the stop for passengers")
    pickup_type = models.CharField(
        max_length=1,
        blank=True,
        choices=(('0', 'Regularly scheduled pickup'), ('1',
                                                       'No pickup available'),
                 ('2', 'Must phone agency to arrange pickup'),
                 ('3', 'Must coordinate with driver to arrange pickup')),
        help_text="How passengers are picked up")
    drop_off_type = models.CharField(
        max_length=1,
        blank=True,
        choices=(('0', 'Regularly scheduled drop off'),
                 ('1', 'No drop off available'),
                 ('2', 'Must phone agency to arrange drop off'),
                 ('3', 'Must coordinate with driver to arrange drop off')),
        help_text="How passengers are picked up")
    shape_dist_traveled = models.FloatField(
        "shape distance traveled",
        null=True,
        blank=True,
        help_text='Distance of stop from start of shape')
    extra_data = JSONField(default={}, blank=True, null=True)

    def __str__(self):
        return "%s-%s-%s" % (self.trip, self.stop.stop_id, self.stop_sequence)

    class Meta:
        db_table = 'gtfs_stop_time'
        app_label = 'multigtfs'

    _column_map = (('trip_id', 'trip__trip_id'),
                   ('arrival_time', 'arrival_time'), ('departure_time',
                                                      'departure_time'),
                   ('stop_id', 'stop__stop_id'), ('stop_sequence',
                                                  'stop_sequence'),
                   ('stop_headsign', 'stop_headsign'), ('pickup_type',
                                                        'pickup_type'),
                   ('drop_off_type', 'drop_off_type'), ('shape_dist_traveled',
                                                        'shape_dist_traveled'))
    _filename = 'stop_times.txt'
    _rel_to_feed = 'trip__route__feed'
    _sort_order = ('trip__trip_id', 'stop_sequence')
    _unique_fields = ('trip_id', 'stop_sequence')
Exemple #17
0
class MsgLog(models.Model):
    authevent_id = models.IntegerField()
    receiver = models.CharField(max_length=255)
    msg = JSONField()
    created = models.DateTimeField(auto_now_add=True)
Exemple #18
0
class UploadLayer(models.Model):
    """Layers stored in an uploaded data set.
    """
    upload = models.ForeignKey(UploadedData, null=True, blank=True)
    upload_file = models.ForeignKey(UploadFile, null=True, blank=True)
    index = models.IntegerField(default=0)
    # *deprecated* name of the layer, sometimes other data
    name = models.CharField(max_length=64, null=True)
    fields = JSONField(null=True, default={})
    content_type = models.ForeignKey(ContentType, blank=True, null=True)
    object_id = models.PositiveIntegerField(blank=True, null=True)
    layer = GenericForeignKey('content_type', 'object_id')
    configuration_options = JSONField(null=True)
    import_status = models.CharField(max_length=15, blank=True, null=True)
    task_id = models.CharField(max_length=36, blank=True, null=True)
    feature_count = models.IntegerField(null=True, blank=True)
    # Name of the layer as known in the file/package/endpoint it came from.
    internal_layer_name = models.CharField(max_length=64, null=True)
    # Geonode-wide unique name for layer.
    layer_name = models.CharField(max_length=64, null=True)
    layer_type = models.CharField(max_length=10, null=True)

    @property
    def file_name(self):
        if not self.upload_file:
            return None
        return self.upload_file.name

    @property
    def file_type(self):
        """A layer's 'file type' - really the file type of the file it is in.
        """
        if not self.upload_file:
            return None
        return self.upload_file.file_type

    @property
    def layer_data(self):
        """
        Serialized information about the layer.
        """
        if not self.layer:
            return

        return {'title': self.layer.title, 'url': self.layer.get_absolute_url(), 'id': self.layer.id}

    @property
    def description(self):
        """
        Serialized description of the layer.
        """

        params = dict(name=self.name, fields=self.fields, imported_layer=None, index=self.index, id=self.id)

        if self.layer:
            params['imported_layer'] = {'typename': self.layer.typename,
                                        'name': self.layer.name,
                                        'url': self.layer.get_absolute_url()}
        return params

    @property
    def status(self):
        """
        Returns the status of the import of this UploadedLayer.
        """
        s = 'UNKNOWN' if self.import_status is None else self.import_status

        return s

    @property
    def import_full_error(self):
        """
        Returns the last error created for an import task
        """
        try:
            ue = UploadException.objects.get(task_id=self.task_id)
        except:
            ue = None
        return ue
    
    @property
    def import_error(self):
        if self.import_full_error is None:
            return None
        e = str(self.import_full_error)
        if re.search('Runtime Error: ', e):
            e = e.split('Runtime Error: ')[1]
        if re.search('layer creation option', e):
            return 'Layer with this name already imported.'
        if re.search('duplicate key', e):
            m = re.search('Key \(([^)]+)\)', e)
            if m:
                col = m.group(1)
                return 'Duplicate key found in column %s' % (col)
        return e

    class Meta:
        ordering = ('index',)
Exemple #19
0
class AbstractTemplate(ShareableOrgMixinUniqueName, BaseConfig):
    """
    Abstract model implementing a
    netjsonconfig template
    """

    tags = TaggableManager(
        through=get_model_name('config', 'TaggedTemplate'),
        blank=True,
        help_text=_('A comma-separated list of template tags, may be used '
                    'to ease auto configuration with specific settings (eg: '
                    '4G, mesh, WDS, VPN, ecc.)'),
    )
    vpn = models.ForeignKey(
        get_model_name('config', 'Vpn'),
        verbose_name=_('VPN'),
        blank=True,
        null=True,
        on_delete=models.CASCADE,
    )
    type = models.CharField(
        _('type'),
        max_length=16,
        choices=TYPE_CHOICES,
        default='generic',
        db_index=True,
        help_text=_('template type, determines which features are available'),
    )
    default = models.BooleanField(
        _('enabled by default'),
        default=False,
        db_index=True,
        help_text=
        _('whether new configurations will have this template enabled by default'
          ),
    )
    required = models.BooleanField(
        _('required'),
        default=False,
        db_index=True,
        help_text=
        _('if checked, will force the assignment of this template to all the '
          'devices of the organization (if no organization is selected, it will '
          'be required for every device in the system)'),
    )
    auto_cert = models.BooleanField(
        _('auto certificate'),
        default=default_auto_cert,
        db_index=True,
        help_text=_('whether x509 client certificates should '
                    'be automatically managed behind the scenes '
                    'for each configuration using this template, '
                    'valid only for the VPN type'),
    )
    default_values = JSONField(
        _('Default Values'),
        default=dict,
        blank=True,
        help_text=_('A dictionary containing the default '
                    'values for the variables used by this '
                    'template; these default variables will '
                    'be used during schema validation.'),
        load_kwargs={'object_pairs_hook': OrderedDict},
        dump_kwargs={'indent': 4},
    )
    __template__ = True

    class Meta:
        abstract = True
        verbose_name = _('template')
        verbose_name_plural = _('templates')
        unique_together = (('organization', 'name'), )

    def save(self, *args, **kwargs):
        """
        modifies status of related configs
        if key attributes have changed (queries the database)
        """
        update_related_config_status = False
        if not self._state.adding:
            current = self.__class__.objects.get(pk=self.pk)
            for attr in ['backend', 'config']:
                if getattr(self, attr) != getattr(current, attr):
                    update_related_config_status = True
                    break
        # save current changes
        super().save(*args, **kwargs)
        # update relations
        if update_related_config_status:
            transaction.on_commit(
                lambda: update_template_related_config_status.delay(self.pk))

    def _update_related_config_status(self):
        # use atomic to ensure any code bound to
        # be executed via transaction.on_commit
        # is executed after the whole block
        with transaction.atomic():
            changing_status = list(
                self.config_relations.exclude(status='modified').values_list(
                    'pk', flat=True))
            for config in self.config_relations.select_related(
                    'device').iterator():
                # config modified signal sent regardless
                config._send_config_modified_signal(
                    action='related_template_changed')
                # config status changed signal sent only if status changed
                if config.pk in changing_status:
                    config._send_config_status_changed_signal()
            self.config_relations.exclude(status='modified').update(
                status='modified')

    def clean(self, *args, **kwargs):
        """
        * validates org relationship of VPN if present
        * validates default_values field
        * ensures VPN is selected if type is VPN
        * clears VPN specific fields if type is not VPN
        * automatically determines configuration if necessary
        * if flagged as required forces it also to be default
        """
        self._validate_org_relation('vpn')
        if not self.default_values:
            self.default_values = {}
        if not isinstance(self.default_values, dict):
            raise ValidationError({
                'default_values':
                _('the supplied value is not a JSON object')
            })
        if self.type == 'vpn' and not self.vpn:
            raise ValidationError({
                'vpn':
                _('A VPN must be selected when template type is "VPN"')
            })
        elif self.type != 'vpn':
            self.vpn = None
            self.auto_cert = False
        if self.type == 'vpn' and not self.config:
            self.config = self.vpn.auto_client(auto_cert=self.auto_cert)
        if self.required and not self.default:
            self.default = True
        super().clean(*args, **kwargs)
        if not self.config:
            raise ValidationError(
                _('The configuration field cannot be empty.'))

    def get_context(self, system=False):
        context = {}
        if self.default_values and not system:
            context = copy(self.default_values)
        context.update(super().get_context())
        return context

    def get_system_context(self):
        system_context = self.get_context(system=True)
        return OrderedDict(sorted(system_context.items()))

    def clone(self, user):
        clone = copy(self)
        clone.name = self.__get_clone_name()
        clone._state.adding = True
        clone.pk = None
        # avoid cloned templates to be flagged as default
        # to avoid potential unwanted duplications in
        # newly registrated devices
        clone.default = False
        clone.full_clean()
        clone.save()
        ct = ContentType.objects.get(model='template')
        LogEntry.objects.log_action(
            user_id=user.id,
            content_type_id=ct.pk,
            object_id=clone.pk,
            object_repr=clone.name,
            action_flag=ADDITION,
        )
        return clone

    def __get_clone_name(self):
        name = '{} (Clone)'.format(self.name)
        index = 2
        while self.__class__.objects.filter(name=name).count():
            name = '{} (Clone {})'.format(self.name, index)
            index += 1
        return name
Exemple #20
0
class ProductVariant(models.Model, Item):
    sku = models.CharField(pgettext_lazy('Variant field', 'SKU'),
                           max_length=32,
                           unique=True)
    name = models.CharField(pgettext_lazy('Variant field', 'variant name'),
                            max_length=100,
                            blank=True)
    price_override = PriceField(pgettext_lazy('Variant field',
                                              'price override'),
                                currency=settings.DEFAULT_CURRENCY,
                                max_digits=12,
                                decimal_places=2,
                                blank=True,
                                null=True)
    weight_override = WeightField(pgettext_lazy('Variant field',
                                                'weight override'),
                                  unit=settings.DEFAULT_WEIGHT,
                                  max_digits=6,
                                  decimal_places=2,
                                  blank=True,
                                  null=True)
    product = models.ForeignKey(Product, related_name='variants')
    attributes = JSONField(pgettext_lazy('Variant field', 'attributes'),
                           default={})

    objects = InheritanceManager()

    class Meta:
        app_label = 'product'

    def __str__(self):
        return self.name or self.sku

    def get_weight(self):
        return self.weight_override or self.product.weight

    def check_quantity(self, quantity):
        available_quantity = self.get_stock_quantity()
        if quantity > available_quantity:
            raise InsufficientStock(self)

    def get_stock_quantity(self):
        return sum([stock.quantity for stock in self.stock.all()])

    def get_price_per_item(self, discounts=None, **kwargs):
        price = self.price_override or self.product.price
        if discounts:
            discounts = list(get_product_discounts(self, discounts, **kwargs))
            if discounts:
                modifier = max(discounts)
                price += modifier
        return price

    def get_absolute_url(self):
        slug = self.product.get_slug()
        product_id = self.product.id
        return reverse('product:details',
                       kwargs={
                           'slug': slug,
                           'product_id': product_id
                       })

    def as_data(self):
        return {
            'product_name': str(self),
            'product_id': self.product.pk,
            'variant_id': self.pk,
            'unit_price': str(self.get_price_per_item().gross)
        }

    def is_shipping_required(self):
        return True

    def is_in_stock(self):
        return any(
            [stock_item.quantity > 0 for stock_item in self.stock.all()])

    def get_attribute(self, pk):
        return self.attributes.get(str(pk))

    def display_variant(self, attributes=None):
        if attributes is None:
            attributes = self.product.attributes.all()
        values = get_attributes_display_map(self, attributes).values()
        if values:
            return ', '.join([smart_text(value) for value in values])
        else:
            return smart_text(self)

    def display_product(self, attributes=None):
        return '%s (%s)' % (smart_text(
            self.product), self.display_variant(attributes=attributes))

    def select_stockrecord(self):
        # By default selects stock with lowest cost price
        stock = sorted(self.stock.all(),
                       key=lambda stock: stock.cost_price,
                       reverse=True)
        if stock:
            return stock[0]

    def get_cost_price(self):
        stock = self.select_stockrecord()
        if stock:
            return stock.cost_price
Exemple #21
0
class JSONFieldTestModel(models.Model):
    json = JSONField(u"test", null=True, blank=True)

    class Meta:
        app_label = 'jsonfield'
Exemple #22
0
class GrangerCausalityConn(models.Model):
    imports = JSONField()

    def __unicode__(self):
        return str(self.imports)
Exemple #23
0
class User(AbstractUser):

    email = models.EmailField('email address', unique=True)
    is_active = models.BooleanField(
        _('Valid'),
        default=True,
        help_text=_(
            'Designates this as a valid user account (eg. not spammer or fake). '
            'Un-checking this will prevent user from being able to login and '
            'is preferable to deleting accounts.'),
    )
    is_osedev = models.BooleanField(
        _('OSE Developer'),
        default=False,
        help_text=_('Designates whether the user has been approved as an '
                    'OSE Developer at some point in time. Adds their logged '
                    'time to global development effort.'),
    )
    is_current = models.BooleanField(
        _('Current'),
        default=False,
        help_text=_('Requires that OSE Developer status is also checked. '
                    'Active OSE Developer is someone who is currently an '
                    'active contributor to OSE.'),
    )
    location = models.CharField(
        _('Location'),
        max_length=512,
        blank=True,
        help_text=
        _('Used with Google Maps API to get a lat/long for mapping contributors.'
          ),
    )
    location_details = JSONField(null=True)
    latitude = models.DecimalField(_("Latitude"),
                                   null=True,
                                   max_digits=9,
                                   decimal_places=6)
    longitude = models.DecimalField(_("Longitude"),
                                    null=True,
                                    max_digits=9,
                                    decimal_places=6)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__original_location = self.location

    def __str__(self):
        return "{} ({})".format(self.get_full_name(), self.get_username())

    def clean(self):

        if self.is_current and not self.is_osedev:
            raise ValidationError({
                'is_current':
                _("To enable current status a user must also be an OSE Developer."
                  )
            })

        if not self.location:
            self.latitude = None
            self.longitude = None
            self.location_details = None
        elif settings.GEOCODE_ENABLED and settings.GOOGLE_API_KEY:
            if not self.__original_location or (self.__original_location !=
                                                self.location):
                gmaps = googlemaps.Client(key=settings.GOOGLE_API_KEY)
                self.location_details = gmaps.geocode(self.location)
                if self.location_details:
                    self.latitude = self.location_details[0]['geometry'][
                        'location']['lng']
                    self.longitude = self.location_details[0]['geometry'][
                        'location']['lat']
Exemple #24
0
class GrangerRan(models.Model):
    date = models.CharField(max_length=100)
    imports = JSONField()

    def __unicode__(self):
        return str(self.date)
Exemple #25
0
class Site(models.Model):
    identifier = models.CharField("ID", max_length=255)
    name = models.CharField(db_index=True, max_length=255)
    type = models.ForeignKey(SiteType,
                             verbose_name='Type of Site',
                             related_name="sites",
                             null=True,
                             blank=True,
                             on_delete=models.SET_NULL)
    phone = models.CharField(max_length=255, blank=True, null=True)
    address = models.TextField(blank=True, null=True)
    public_desc = models.TextField("Public Description", blank=True, null=True)
    additional_desc = models.TextField("Additional Description",
                                       blank=True,
                                       null=True)
    project = models.ForeignKey(Project, related_name='sites')
    logo = models.ImageField(upload_to="logo",
                             default="logo/default_site_image.png",
                             max_length=500)
    is_active = models.BooleanField(db_index=True, default=True)
    location = PointField(geography=True, srid=4326, blank=True, null=True)
    is_survey = models.BooleanField(default=False)
    date_created = models.DateTimeField(auto_now_add=True, blank=True)
    date_modified = models.DateTimeField(auto_now=True, blank=True)
    region = models.ForeignKey(Region,
                               related_name='regions',
                               blank=True,
                               null=True)
    site_meta_attributes_ans = JSONField(default=dict)
    all_ma_ans = JSONField(default=dict)
    site_featured_images = JSONField(default=dict)
    current_progress = models.FloatField(default=0.0)
    current_status = models.IntegerField(default=0)
    enable_subsites = models.BooleanField(default=False)
    site = models.ForeignKey('self',
                             blank=True,
                             null=True,
                             related_name="sub_sites")
    weight = models.IntegerField(default=0, blank=True)
    all_objects = SiteAllManager()
    objects = SiteManager()

    logs = GenericRelation('eventlog.FieldSightLog')

    class Meta:
        ordering = ['-is_active', '-id']
        unique_together = [
            ('identifier', 'project', 'is_active'),
        ]

    @property
    def latitude(self):
        if self.location:
            return self.location.y

    @property
    def longitude(self):
        if self.location:
            return self.location.x

    def update_status(self):
        try:
            status = self.site_instances.order_by('-date').first().form_status
        except:
            status = 0
        self.current_status = status
        self.save()

    def getname(self):
        return self.name

    def __unicode__(self):
        return u'{}'.format(self.name)

    @property
    def get_supervisors(self):
        return self.site_roles.all()

    @property
    def get_supervisor_id(self):
        staffs = list(self.site_roles.filter(group__name="Site Supervisor"))
        if staffs:
            return [role.user.id for role in staffs]
        return []

    def get_organization_name(self):
        return self.project.organization.name

    def get_project_name(self):
        return self.project.name

    def get_site_type(self):
        return self.type.name

    def update_current_progress(self):
        if not self.enable_subsites:
            from onadata.apps.fieldsight.tasks import update_current_progress_site
            update_current_progress_site.apply_async(
                kwargs={'site_id': self.id}, countdown=5)

    def progress(self):
        from onadata.apps.fieldsight.utils.progress import default_progress
        return default_progress(self, self.project)

    @property
    def site_progress(self):
        return self.progress()

    @property
    def status(self):
        if self.site_instances.filter(form_status=1).count():
            return 1
        elif self.site_instances.filter(form_status=2).count():
            return 2
        elif self.site_instances.filter(form_status=0).count():
            return 0
        elif self.site_instances.filter(form_status=3).count():
            return 3
        return 4

    @property
    def site_status(self):
        forms = self.project.project_forms.filter(is_staged=True,
                                                  is_deleted=False,
                                                  is_deployed=True)
        try:
            return self.site_instances.filter(project_fxf__in=forms).order_by(
                '-instance_id')[0].get_abr_form_status()
        except:
            return "No Submission"

    def get_site_submission(self):
        instances = self.site_instances.all().order_by('-date')
        outstanding, flagged, approved, rejected = [], [], [], []
        for submission in instances:
            if submission.form_status == 0:
                outstanding.append(submission)
            elif submission.form_status == 1:
                rejected.append(submission)
            elif submission.form_status == 2:
                flagged.append(submission)
            elif submission.form_status == 3:
                approved.append(submission)

        return outstanding, flagged, approved, rejected

    def get_site_submission_count(self):
        instances = self.site_instances.all().order_by('-date')
        outstanding, flagged, approved, rejected = 0, 0, 0, 0
        for submission in instances:
            if submission.form_status == 0:
                outstanding += 1
            elif submission.form_status == 1:
                rejected += 1
            elif submission.form_status == 2:
                flagged += 1
            elif submission.form_status == 3:
                approved += 1
        response = {}
        response['outstanding'] = outstanding
        response['rejected'] = rejected
        response['flagged'] = flagged
        response['approved'] = approved

        return response

    def get_parent_sites(self):
        site = self
        site_ids = Site.objects.select_related('site').filter(
            id=site.id).values('id', 'site', 'site__site')
        parent_sites = []
        parent_sites.extend([
            site_ids[0]['id'], site_ids[0]['site'], site_ids[0]['site__site']
        ])

        return parent_sites

    def get_absolute_url(self):
        return "/fieldsight/application/#/site-dashboard/{}".format(self.pk)
Exemple #26
0
class BaseOrder(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             on_delete=models.CASCADE)
    name = models.CharField(max_length=100, verbose_name='상품명')
    amount = models.PositiveIntegerField(verbose_name='결제금액')
    merchant_uid = models.UUIDField(default=uuid4, editable=False)
    imp_uid = models.CharField(max_length=100, blank=True)
    meta = JSONField(blank=True, default={})
    status = models.CharField(max_length=9,
                              choices=(
                                  ('ready', '미결제'),
                                  ('paid', '결제완료'),
                                  ('cancelled', '결제취소'),
                                  ('failed', '결제실패'),
                              ),
                              default='ready',
                              db_index=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    is_ready = property(lambda self: self.status == 'ready')
    is_paid = property(lambda self: self.status == 'paid')
    is_paid_ok = property(lambda self: self.status == 'paid' and self.amount ==
                          self.meta.get('amount'))
    is_cancelled = property(lambda self: self.status == 'cancelled')
    is_failed = property(lambda self: self.status == 'failed')
    receipt_url = named_property('영수증')(
        lambda self: self.meta.get('receipt_url'))
    cancel_reason = named_property('취소이유')(
        lambda self: self.meta.get('cancel_reason'))
    fail_reason = named_property('실패이유')(
        lambda self: self.meta.get('fail_reason', ''))
    paid_at = named_property('결제일시')(
        lambda self: timestamp_to_datetime(self.meta.get('paid_at')))
    failed_at = named_property('실패일시')(
        lambda self: timestamp_to_datetime(self.meta.get('failed_at')))
    cancelled_at = named_property('취소일시')(
        lambda self: timestamp_to_datetime(self.meta.get('cancelled_at')))

    class Meta:
        ordering = ('-id', )

    @named_property('결제금액')
    def amount_html(self):
        return mark_safe('<div style="float: right;">{0}</div>'.format(
            intcomma(self.amount)))

    @named_property('처리결과')
    def status_html(self):
        cls, text_color = '', ''
        help_text = ''
        if self.is_ready:
            cls, text_color = 'fa fa-shopping-cart', '#ccc'
        elif self.is_paid_ok:
            cls, text_color = 'fa fa-check-circle', 'green'
        elif self.is_cancelled:
            cls, text_color = 'fa fa-times', 'gray'
            help_text = self.cancel_reason
        elif self.is_failed:
            cls, text_color = 'fa fa-ban', 'red'
            help_text = self.fail_reason
        html = '''
            <span style="color: {text_color};" title="this is title">
                <i class="{class_names}"></i>
                {label}
            </span>'''.format(class_names=cls,
                              text_color=text_color,
                              label=self.get_status_display())
        if help_text:
            html += '<br/>' + help_text
        return mark_safe(html)

    @named_property('영수증 링크')
    def receipt_link(self):
        if self.is_paid_ok and self.receipt_url:
            return mark_safe('<a href="{0}" target="_blank">영수증</a>'.format(
                self.receipt_url))

    @property
    def api(self):
        'Iamport Client 인스턴스'
        return Iamport(settings.IAMPORT_API_KEY, settings.IAMPORT_API_SECRET)

    def update(self, commit=True, meta=None):
        '결재내역 갱신'
        if self.imp_uid:
            #self.meta = meta or self.api.find(imp_uid=self.imp_uid)
            try:
                self.meta = meta or self.api.find(imp_uid=self.imp_uid)
            except Iamport.HttpError:
                raise Http404('Not found {}'.format(self.imp_uid))
            # merchant_uid는 반드시 매칭되어야 합니다.
            assert str(self.merchant_uid) == self.meta['merchant_uid']
            self.status = self.meta['status']
        if commit:
            self.save()

    def cancel(self, reason=None, commit=True):
        '결제내역 취소'
        try:
            meta = self.api.cancel(reason, imp_uid=self.imp_uid)
            assert str(self.merchant_uid) == self.meta['merchant_uid']
            self.update(commit=commit, meta=meta)
        except Iamport.ResponseError as e:  # 취소시 오류 예외처리(이미 취소된 결제는 에러가 발생함)
            self.update(commit=commit)
        if commit:
            self.save()
Exemple #27
0
class MapStoreData(models.Model):
    blob = JSONField(null=False, default={})
    resource = models.ForeignKey(MapStoreResource,
                                 null=False,
                                 blank=False,
                                 on_delete=models.CASCADE)
Exemple #28
0
class TableSettings(models.Model):
    user = models.ForeignKey(User)
    name = models.CharField(max_length=128)
    value = JSONField()
    per_page = models.IntegerField()
Exemple #29
0
class Translation(DirtyFieldsMixin, models.Model):
    entity = models.ForeignKey(Entity)
    locale = models.ForeignKey(Locale)
    user = models.ForeignKey(User, null=True, blank=True)
    string = models.TextField()
    # 0=zero, 1=one, 2=two, 3=few, 4=many, 5=other, null=no plural forms
    plural_form = models.SmallIntegerField(null=True, blank=True)
    date = models.DateTimeField(auto_now_add=True)
    approved = models.BooleanField(default=False)
    approved_user = models.ForeignKey(User,
                                      related_name='approvers',
                                      null=True,
                                      blank=True)
    approved_date = models.DateTimeField(null=True, blank=True)
    fuzzy = models.BooleanField(default=False)
    deleted = models.DateTimeField(default=None, null=True)

    # extra stores data that we want to save for the specific format
    # this translation is stored in, but that we otherwise don't care
    # about.
    extra = JSONField(default=extra_default)

    # Due to https://code.djangoproject.com/ticket/14891,
    # TranslationManager will be used for the reverse FK from entities,
    # e.g. entity.translation_set. Deleted translations cannot be
    # accessed via that relation.
    objects = TranslationManager()
    deleted_objects = DeletedTranslationManager()

    def __unicode__(self):
        return self.string

    def save(self, imported=False, *args, **kwargs):
        super(Translation, self).save(*args, **kwargs)

        # Only one translation can be approved at a time for any
        # Entity/Locale.
        if self.approved:
            (Translation.objects.filter(
                entity=self.entity,
                locale=self.locale,
                plural_form=self.plural_form).exclude(pk=self.pk).update(
                    approved=False, approved_user=None, approved_date=None))

        if not imported:
            # Update stats AFTER changing approval status.
            update_stats(self.entity.resource, self.locale)

            # Whenever a translation changes, mark the entity as having
            # changed in the appropriate locale. We could be smarter about
            # this but for now this is fine.
            self.entity.mark_changed(self.locale)

    def mark_for_deletion(self):
        self.deleted = timezone.now()
        self.save()

    def delete(self, stats=True, *args, **kwargs):
        super(Translation, self).delete(*args, **kwargs)
        if stats:
            update_stats(self.entity.resource, self.locale)

    def serialize(self):
        return {
            'pk': self.pk,
            'string': self.string,
            'approved': self.approved,
            'fuzzy': self.fuzzy,
        }
class Country(models.Model):
    code = models.CharField(max_length=2, unique=True)
    alpha_3_code = models.CharField(max_length=3, blank=True, default="")
    name = models.CharField(max_length=75)
    region = models.ForeignKey(Region,
                               null=True,
                               blank=True,
                               related_name="countries")
    population = models.IntegerField(null=True, blank=True)
    primary_networks = models.ManyToManyField(
        Network, blank=True, db_table='uw_country_primary_networks')
    extra_data = JSONField(blank=True)

    tracker = FieldTracker()

    class Meta:
        db_table = 'uw_country'

    def gateway_language(self):
        if not hasattr(self, "_gateway_language"):
            data = self.extra_data
            if not isinstance(data, dict):
                data = {}
            self._gateway_language = next(
                iter(Language.objects.filter(
                    code=data.get("gateway_language"))), None)
        return self._gateway_language

    def gateway_languages(self, with_primary=True):
        gl = self.gateway_language()
        if gl:
            ogls = [gl]
        else:
            ogls = []
        for lang in self.language_set.all():
            if lang.gateway_flag and lang not in ogls:
                ogls.append(lang)
            elif lang.gateway_language and lang.gateway_language not in ogls:
                ogls.append(lang.gateway_language)
        if not with_primary and gl:
            ogls.remove(gl)
        return ogls

    @classmethod
    def regions(cls):
        qs = cls.objects.all().values_list("region", flat=True).distinct()
        qs = qs.order_by("region.name")
        return qs

    @classmethod
    def gateway_data(cls):
        with_gateways = cls.objects.filter(
            language__gateway_language__isnull=False).distinct()
        without_gateways = cls.objects.exclude(pk__in=with_gateways)
        data = {
            x.code: {
                "obj": x,
                "gateways": defaultdict(lambda: [])
            }
            for x in with_gateways
        }
        data.update({
            x.code: {
                "obj": x,
                "gateways": {
                    "n/a": list(x.language_set.all())
                }
            }
            for x in without_gateways
        })
        for country in with_gateways:
            for lang in country.language_set.all():
                if lang.gateway_language:
                    data[country.code]["gateways"][
                        lang.gateway_language.code].append(lang)
                else:
                    data[country.code]["gateways"]["n/a"].append(lang)
        return data

    def __str__(self):
        return self.name