コード例 #1
0
class AutomaticUrl2(Model):
    foo = ForeignKey(AutomaticUrl, on_delete=CASCADE)

    class Meta:
        ordering = ('pk',)
コード例 #2
0
class Comment(models.Model):
    """
    This model represents a comment which can be added to any post by an anonymous visitor
    """
    name = CharField(max_length=250)
    email = models.EmailField()
    content = TextField(max_length=2000, blank=True)
    publishing_date = DateTimeField(default=timezone.now)

    # These two will not be settable manually.
    # The "hash_id" is an integer hash value which is computed from the name of the comment (the pseudonym the author
    # chooses) and the entered email address.
    # In the first step, the author field can only be manipulated from the admin backend which means that only
    # registered admins could change this.
    hash_id = models.CharField(max_length=3, null=True, blank=True)
    author = ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)

    # https://docs.djangoproject.com/en/3.2/ref/contrib/contenttypes/x
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    entry = GenericForeignKey('content_type', 'object_id')

    # This is intended to at some point help with moderating. The idea is to have a filter check for obvious problems
    # with the comments like swearing etc. and then automatically set active to false, which means they wont be
    # displayed and wait for further approval in the admin backend.
    active = BooleanField(default=False)

    # So here are my thoughts about images: I think anonymous commenters pose quite the problem: Beside the usual stuff
    # like spamming etc. I am worried about identity theft: On default someone could just copy the comment name of
    # someone else and pretend that it is the same person. So the plan is to also have the commenter enter the email
    # address for every comment and then build a hash value from the public comment name and the email address which is
    # only saved on the server. Based on this hash value (there will be relatively few possible values) a specific Anon
    # profile picture will be set. This will ensure that the same person could be identified by the profile picture.
    image = FilerImageField(on_delete=models.CASCADE, null=True, blank=True)

    @property
    def html(self):
        return self.content.replace('\n', '<br>')

    def save(self, *args, **kwargs):
        if not self.hash_id:
            self.hash_id = self.generate_hash_id()

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

    def get_image_url(self):
        if self.author:
            return self.author.image.url

        image_file = f'comment_images/{self.hash_id}.jpg'
        return static(image_file)

    def generate_hash_id(self) -> str:
        hash_base = f'{self.name} - {self.email.lower()}'
        hash_string = str(hash(hash_base))
        hash_id = hash_string[-3:]
        return hash_id

    def get_shortened_content(self, length=30) -> str:
        """
        Returns the first *length* characters of the content string of the comment. If the comment is shorter than this
        length, returns the whole content instead.

        :param length: The int amount of the first characters of the content to return
        :return:
        """
        if len(self.content) <= length:
            return self.content
        else:
            shortened_content = self.content[0:length] + '...'
            return shortened_content

    # This is curious: We assign a property "short_description" to the method instance. This is made to support the
    # usage of this method in the admin form list display. If the function has this "short description" string property
    # then this string will be displayed as the title of the column in the list display.
    get_shortened_content.short_description = 'Content'
コード例 #3
0
class App(TranslatableModel):
    objects = AppManager()
    id = CharField(max_length=256, unique=True, primary_key=True,
                   verbose_name=_('ID'),
                   help_text=_('app ID, identical to folder name'))
    categories = ManyToManyField('Category', verbose_name=_('Category'))
    translations = TranslatedFields(
        name=CharField(max_length=256, verbose_name=_('Name'),
                       help_text=_('Rendered app name for users')),
        summary=CharField(max_length=256, verbose_name=_('Summary'),
                          help_text=_(
                              'Short text describing the app\'s purpose')),
        description=TextField(verbose_name=_('Description'), help_text=_(
            'Will be rendered as Markdown'))
    )
    # resources
    user_docs = URLField(max_length=256, blank=True,
                         verbose_name=_('User documentation URL'))
    admin_docs = URLField(max_length=256, blank=True,
                          verbose_name=_('Admin documentation URL'))
    developer_docs = URLField(max_length=256, blank=True,
                              verbose_name=_('Developer documentation URL'))
    issue_tracker = URLField(max_length=256, blank=True,
                             verbose_name=_('Issue tracker URL'))
    website = URLField(max_length=256, blank=True, verbose_name=_('Homepage'))
    created = DateTimeField(auto_now_add=True, editable=False,
                            verbose_name=_('Created at'))
    last_modified = DateTimeField(auto_now=True, editable=False, db_index=True,
                                  verbose_name=_('Updated at'))
    owner = ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('App owner'),
                       on_delete=CASCADE, related_name='owned_apps')
    co_maintainers = ManyToManyField(settings.AUTH_USER_MODEL, blank=True,
                                     verbose_name=_('Co-Maintainers'),
                                     related_name='co_maintained_apps')
    authors = ManyToManyField('AppAuthor', blank=True, related_name='apps',
                              verbose_name=_('App authors'))
    is_featured = BooleanField(verbose_name=_('Featured'), default=False)
    rating_recent = FloatField(verbose_name=_('Recent rating'), default=0.5)
    rating_overall = FloatField(verbose_name=_('Overall rating'), default=0.5)
    rating_num_recent = IntegerField(
        verbose_name=_('Number of recently submitted ratings'), default=0)
    rating_num_overall = IntegerField(
        verbose_name=_('Number of overall submitted ratings'), default=0)
    last_release = DateTimeField(editable=False, db_index=True,
                                 verbose_name=_('Last release at'),
                                 default=timezone.now)
    certificate = TextField(verbose_name=_('Certificate'))
    ownership_transfer_enabled = BooleanField(
        verbose_name=_('Ownership transfer enabled'), default=False,
        help_text=_('If enabled, a user can try to register the same app '
                    'again using the public certificate and signature. If he '
                    'does, the app will be transferred to him.'))

    class Meta:
        verbose_name = _('App')
        verbose_name_plural = _('Apps')

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

    def can_update(self, user: User) -> bool:
        return self.owner == user or user in self.co_maintainers.all()

    def can_delete(self, user: User) -> bool:
        return self.owner == user

    def _get_grouped_releases(self, get_release_func):
        releases = NextcloudRelease.objects.all()
        versions = map(lambda r: r.version, releases)
        compatible_releases = map(lambda v: (v, get_release_func(v)), versions)
        grouped_releases = group_by_main_version(dict(compatible_releases))
        # deduplicate releases
        result = {}
        for version, releases in grouped_releases.items():
            result[version] = list(distinct(releases, lambda r: r.version))
        return result

    def releases_by_platform_v(self):
        """Looks up all compatible stable releases for each platform
        version.

        Example of returned dict:

        {'9.1': [<AppRelease object>, <AppRelease object>],
        '9.0': [<AppRelease object>]}

        :return dict with all compatible stable releases for each platform
                version.
        """
        return self._get_grouped_releases(self.compatible_releases)

    def unstable_releases_by_platform_v(self):
        """Looks up all compatible unstable releases for each platform version.

        Example of returned dict:

        {'9.1': [<AppRelease object>, <AppRelease object>],
        '9.0': [<AppRelease object>]}

        :return dict with all compatible unstable releases for each platform
                version.
        """
        return self._get_grouped_releases(self.compatible_unstable_releases)

    def latest_releases_by_platform_v(self):
        """Looks up the latest stable and unstable release for each platform
        version.

        Example of returned dict:

        {'9.1': {
            'stable': <AppRelease object>,
            'unstable': <AppRelease object>
        },
        '9.0': {
            'stable': <AppRelease object>
        }}

        :return dict with the latest stable and unstable release for each
                platform version.
        """
        stable = self.releases_by_platform_v()
        unstable = self.unstable_releases_by_platform_v()

        def filter_latest(pair):
            version, releases = pair
            return (version, self._latest(releases))

        latest_stable = dict(map(filter_latest, stable.items()))
        latest_unstable = dict(map(filter_latest, unstable.items()))
        all_versions = set(chain(latest_stable.keys(), latest_unstable.keys()))

        def stable_or_unstable_releases(ver):
            return (ver, {
                'stable': latest_stable.get(ver, None),
                'unstable': latest_unstable.get(ver, None)
            })

        return dict(map(stable_or_unstable_releases, all_versions))

    def compatible_releases(self, platform_version, inclusive=True):
        """Returns all stable releases of this app that are compatible
        with the given platform version.

        :param inclusive: Use inclusive version check (see
                          AppRelease.is_compatible()).
        :return a sorted list of all compatible stable releases.
        """

        return sorted(
            filter(
                lambda r: r.is_compatible(platform_version,
                                          inclusive) and not r.is_unstable,
                self.releases.all()),
            key=lambda r: AppSemVer(r.version, r.is_nightly, r.last_modified),
            reverse=True)

    def compatible_unstable_releases(self, platform_version, inclusive=True):
        """Returns all unstable releases of this app that are compatible with
        the given platform version.

        :param inclusive: Use inclusive version check (see
                          AppRelease.is_compatible()).
        :return a sorted list of all compatible unstable releases.
        """

        return sorted(
            filter(
                lambda r: r.is_compatible(platform_version,
                                          inclusive) and r.is_unstable,
                self.releases.all()),
            key=lambda r: AppSemVer(r.version, r.is_nightly, r.last_modified),
            reverse=True)

    def _latest(self, releases):
        try:
            return max(releases,
                       key=lambda r: AppSemVer(r.version, r.is_nightly,
                                               r.last_modified))
        except ValueError:
            return None

    def save(self, *args, **kwargs):
        # If the certificate has changed, delete all releases.
        try:
            if self.pk is not None:
                orig = App.objects.get(pk=self.pk)
                current = self.certificate.replace('\r', '').strip()
                former = orig.certificate.replace('\r', '').strip()
                # for some reason the django admin inserts \r\n for \n so
                # saving a model in the admin with the same cert kills all
                # releases
                if current != former:
                    self.releases.all().delete()
        except self.DoesNotExist:
            pass
        super().save(*args, **kwargs)
コード例 #4
0
class BMC(CleanSave, TimestampedModel):
    """A `BMC` represents an existing 'baseboard management controller'.  For
    practical purposes in MAAS, this is any addressable device that can control
    the power state of Nodes. The BMC associated with a Node is the one
    expected to control its power.

    Power parameters that apply to all nodes controlled by a BMC are stored
    here in the BMC. Those that are specific to different Nodes on the same BMC
    are stored in the Node model instances.

    :ivar ip_address: This `BMC`'s IP Address.
    :ivar power_type: The power type defines which type of BMC this is.
        Its value must match a power driver class name.
    :ivar power_parameters: Some JSON containing arbitrary parameters this
        BMC's power driver requires to function.
    :ivar objects: The :class:`BMCManager`.
    """
    class Meta(DefaultMeta):
        unique_together = ("power_type", "power_parameters", "ip_address")

    objects = Manager()

    bmcs = BMCManager()

    bmc_type = IntegerField(choices=BMC_TYPE_CHOICES,
                            editable=False,
                            default=BMC_TYPE.DEFAULT)

    ip_address = ForeignKey(StaticIPAddress,
                            default=None,
                            blank=True,
                            null=True,
                            editable=False,
                            on_delete=SET_NULL)

    # The possible choices for this field depend on the power types advertised
    # by the rack controllers.  This needs to be populated on the fly, in
    # forms.py, each time the form to edit a node is instantiated.
    power_type = CharField(max_length=10, null=False, blank=True, default='')

    # JSON-encoded set of parameters for power control, limited to 32kiB when
    # encoded as JSON. These apply to all Nodes controlled by this BMC.
    power_parameters = JSONObjectField(max_length=(2**15),
                                       blank=True,
                                       default='')

    # Rack controllers that have access to the BMC by routing instead of
    # having direct layer 2 access.
    routable_rack_controllers = ManyToManyField(
        "RackController",
        blank=True,
        editable=True,
        through="BMCRoutableRackControllerRelationship",
        related_name="routable_bmcs")

    # Values for Pod's.
    #  1. Name of the Pod.
    #  2. List of architectures that a Pod supports.
    #  3. Capabilities that the Pod supports.
    #  4. Total cores in the Pod.
    #  5. Fastest CPU speed in the Pod.
    #  6. Total amount of memory in the Pod.
    #  7. Total amount in bytes of local storage available in the Pod.
    #  8. Total number of available local disks in the Pod.
    #  9. The resource pool machines in the pod should belong to by default.
    #  10. The zone of the Pod.
    #  11. The tags of the Pod.
    #  12. CPU over commit ratio multiplier ('over_commit' capabilities).
    #  13. Memory over commit ratio multiplier ('over_commit' capabilities).
    name = CharField(max_length=255, default='', blank=True, unique=True)
    architectures = ArrayField(TextField(),
                               blank=True,
                               null=True,
                               default=list)
    capabilities = ArrayField(TextField(), blank=True, null=True, default=list)
    cores = IntegerField(blank=False, null=False, default=0)
    cpu_speed = IntegerField(blank=False, null=False, default=0)  # MHz
    memory = IntegerField(blank=False, null=False, default=0)
    local_storage = BigIntegerField(  # Bytes
        blank=False, null=False, default=0)
    local_disks = IntegerField(blank=False, null=False, default=-1)
    iscsi_storage = BigIntegerField(  # Bytes
        blank=False, null=False, default=-1)
    default_pool = ForeignKey(ResourcePool,
                              default=None,
                              null=True,
                              blank=True,
                              editable=True,
                              on_delete=PROTECT)
    zone = ForeignKey(Zone,
                      verbose_name="Physical zone",
                      default=get_default_zone,
                      editable=True,
                      db_index=True,
                      on_delete=SET_DEFAULT)
    tags = ArrayField(TextField(), blank=True, null=True, default=list)
    cpu_over_commit_ratio = FloatField(default=1,
                                       validators=[MinValueValidator(0)])
    memory_over_commit_ratio = FloatField(default=1,
                                          validators=[MinValueValidator(0)])

    def __str__(self):
        return "%s (%s)" % (self.id,
                            self.ip_address if self.ip_address else "No IP")

    def _as(self, model):
        """Create a `model` that shares underlying storage with `self`.

        In other words, the newly returned object will be an instance of
        `model` and its `__dict__` will be `self.__dict__`. Not a copy, but a
        reference to, so that changes to one will be reflected in the other.
        """
        new = object.__new__(model)
        new.__dict__ = self.__dict__
        return new

    def as_bmc(self):
        """Return a reference to self that behaves as a `BMC`."""
        return self._as(BMC)

    def as_pod(self):
        """Return a reference to self that behaves as a `Pod`."""
        return self._as(Pod)

    _as_self = {
        BMC_TYPE.BMC: as_bmc,
        BMC_TYPE.POD: as_pod,
    }

    def as_self(self):
        """Return a reference to self that behaves as its own type."""
        return self._as_self[self.bmc_type](self)

    def delete(self):
        """Delete this BMC."""
        maaslog.info("%s: Deleting BMC", self)
        super(BMC, self).delete()

    def save(self, *args, **kwargs):
        """Save this BMC."""
        super(BMC, self).save(*args, **kwargs)
        # We let name be blank for the initial save, but fix it before the
        # save completes.  This is because set_random_name() operates by
        # trying to re-save the BMC with a random hostname, and retrying until
        # there is no conflict.
        if self.name == '':
            self.set_random_name()

    def set_random_name(self):
        """Set a random `name`."""
        while True:
            self.name = petname.Generate(2, "-")
            try:
                self.save()
            except ValidationError:
                pass
            else:
                break

    def clean(self):
        """ Update our ip_address if the address extracted from our power
        parameters has changed. """
        new_ip = BMC.extract_ip_address(self.power_type, self.power_parameters)
        current_ip = None if self.ip_address is None else self.ip_address.ip
        # Set the ip_address field.  If we have a bracketed address, assume
        # it's IPv6, and strip the brackets.
        if new_ip and new_ip.startswith('[') and new_ip.endswith(']'):
            new_ip = new_ip[1:-1]
        if new_ip != current_ip:
            if new_ip is None:
                self.ip_address = None
            else:
                # Update or create a StaticIPAddress for the new IP.
                try:
                    # This atomic block ensures that an exception within will
                    # roll back only this block's DB changes. This allows us to
                    # swallow exceptions in here and keep all changes made
                    # before or after this block is executed.
                    with transaction.atomic():
                        subnet = Subnet.objects.get_best_subnet_for_ip(new_ip)
                        (self.ip_address,
                         _) = StaticIPAddress.objects.get_or_create(
                             ip=new_ip,
                             defaults={
                                 'alloc_type': IPADDRESS_TYPE.STICKY,
                                 'subnet': subnet,
                             })
                except Exception as error:
                    maaslog.info(
                        "BMC could not save extracted IP "
                        "address '%s': '%s'", new_ip, error)

    @staticmethod
    def scope_power_parameters(power_type, power_params):
        """Separate the global, bmc related power_parameters from the local,
        node-specific ones."""
        if not power_type:
            # If there is no power type, treat all params as node params.
            return ({}, power_params)
        power_driver = PowerDriverRegistry.get_item(power_type)
        if power_driver is None:
            # If there is no power driver, treat all params as node params.
            return ({}, power_params)
        power_fields = power_driver.settings
        if not power_fields:
            # If there is no parameter info, treat all params as node params.
            return ({}, power_params)
        bmc_params = {}
        node_params = {}
        for param_name in power_params:
            power_field = power_driver.get_setting(param_name)
            if (power_field and power_field.get('scope') == SETTING_SCOPE.BMC):
                bmc_params[param_name] = power_params[param_name]
            else:
                node_params[param_name] = power_params[param_name]
        return (bmc_params, node_params)

    @staticmethod
    def extract_ip_address(power_type, power_parameters):
        """ Extract the ip_address from the power_parameters. If there is no
        power_type, no power_parameters, or no valid value provided in the
        power_address field, returns None. """
        if not power_type or not power_parameters:
            # Nothing to extract.
            return None
        power_driver = PowerDriverRegistry.get_item(power_type)
        if power_driver is None:
            maaslog.warning("No power driver for power type %s" % power_type)
            return None
        power_type_parameters = power_driver.settings
        if not power_type_parameters:
            maaslog.warning("No power driver settings for power type %s" %
                            power_type)
            return None
        ip_extractor = power_driver.ip_extractor
        if not ip_extractor:
            maaslog.info("No IP extractor configured for power type %s. "
                         "IP will not be extracted." % power_type)
            return None
        field_value = power_parameters.get(ip_extractor.get('field_name'))
        if not field_value:
            maaslog.warning("IP extractor field_value missing for %s" %
                            power_type)
            return None
        extraction_pattern = ip_extractor.get('pattern')
        if not extraction_pattern:
            maaslog.warning("IP extractor extraction_pattern missing for %s" %
                            power_type)
            return None
        match = re.match(extraction_pattern, field_value)
        if match:
            return match.group('address')
        # no match found - return None
        return None

    def get_layer2_usable_rack_controllers(self, with_connection=True):
        """Return a list of `RackController`'s that have the ability to access
        this `BMC` directly through a layer 2 connection."""
        ip_address = self.ip_address
        if ip_address is None or ip_address.ip is None or ip_address.ip == '':
            return set()

        # The BMC has a valid StaticIPAddress set. Make sure that the subnet
        # is correct for that BMC.
        subnet = Subnet.objects.get_best_subnet_for_ip(ip_address.ip)
        if subnet is not None and self.ip_address.subnet_id != subnet.id:
            self.ip_address.subnet = subnet
            self.ip_address.save()

        # Circular imports.
        from maasserver.models.node import RackController
        return RackController.objects.filter_by_url_accessible(
            ip_address.ip, with_connection=with_connection)

    def get_routable_usable_rack_controllers(self, with_connection=True):
        """Return a list of `RackController`'s that have the ability to access
        this `BMC` through a route on the rack controller."""
        routable_racks = [
            relationship.rack_controller
            for relationship in (self.routable_rack_relationships.all(
            ).select_related("rack_controller")) if relationship.routable
        ]
        if with_connection:
            conn_rack_ids = [client.ident for client in getAllClients()]
            return [
                rack for rack in routable_racks
                if rack.system_id in conn_rack_ids
            ]
        else:
            return routable_racks

    def get_usable_rack_controllers(self, with_connection=True):
        """Return a list of `RackController`'s that have the ability to access
        this `BMC` either using layer2 or routable if no layer2 are available.
        """
        racks = self.get_layer2_usable_rack_controllers(
            with_connection=with_connection)
        if len(racks) == 0:
            # No layer2 routable rack controllers. Use routable rack
            # controllers.
            racks = self.get_routable_usable_rack_controllers(
                with_connection=with_connection)
        return racks

    def get_client_identifiers(self):
        """Return a list of identifiers that can be used to get the
        `rpc.common.Client` for this `BMC`.

        :raise NoBMCAccessError: Raised when no rack controllers have access
            to this `BMC`.
        """
        rack_controllers = self.get_usable_rack_controllers()
        identifers = [controller.system_id for controller in rack_controllers]
        return identifers

    def is_accessible(self):
        """If the BMC is accessible by at least one rack controller."""
        racks = self.get_usable_rack_controllers(with_connection=False)
        return len(racks) > 0

    def update_routable_racks(self, routable_racks_ids,
                              non_routable_racks_ids):
        """Set the `routable_rack_controllers` relationship to the new
        information."""
        BMCRoutableRackControllerRelationship.objects.filter(
            bmc=self.as_bmc()).delete()
        self._create_racks_relationship(routable_racks_ids, True)
        self._create_racks_relationship(non_routable_racks_ids, False)

    def _create_racks_relationship(self, rack_ids, routable):
        """Create `BMCRoutableRackControllerRelationship` for list of
        `rack_ids` and wether they are `routable`."""
        # Circular imports.
        from maasserver.models.node import RackController
        for rack_id in rack_ids:
            try:
                rack = RackController.objects.get(system_id=rack_id)
            except RackController.DoesNotExist:
                # Possible it was delete before this call, but very very rare.
                pass
            BMCRoutableRackControllerRelationship(bmc=self,
                                                  rack_controller=rack,
                                                  routable=routable).save()
コード例 #5
0
ファイル: models.py プロジェクト: roodie-ree/cookcook
class Step(Model):
    number = PositiveSmallIntegerField()
    description = TextField()
    recipe = ForeignKey(Recipe, on_delete=CASCADE)
コード例 #6
0
class Report(Model):
    reporter = ForeignKey('Writer', on_delete=CASCADE)
    article = ForeignKey('Article', on_delete=CASCADE)
コード例 #7
0
ファイル: models.py プロジェクト: gonczor/black-sheep-codes
class Answer(Model):
    question = ForeignKey("TestQuestion", on_delete=CASCADE, related_name="answers")
    text = TextField()
    is_correct = BooleanField(default=False)
コード例 #8
0
ファイル: models.py プロジェクト: ASHCO-2019/developer-portal
class EventSpeaker(Orderable):
    event = ParentalKey("Event", related_name="speaker")
    speaker = ForeignKey("people.Person", on_delete=CASCADE, related_name="+")
    panels = [PageChooserPanel("speaker")]
コード例 #9
0
class Transaction(StatefulTransactionModel):
    # user who participate in this transaction

    # to_u = ForeignKey(
    #     User, on_delete=models.PROTECT, related_name="to_user")
    # we can assume every transaction has a bill
    bill = ForeignKey(Bill, on_delete=models.CASCADE)

    @property
    def owner(self):
        # is the the bill owner can setup this transactions
        return self.bill.owner

    def approve(self):
        """
        @pre: request user == self.from_u
        """
        if self.state == PREPARE:
            # only the prepare state can go be approved
            self.state = APPROVED
            self.save()
            return True
        return False

    def reject(self):
        """
        @pre: request_uesr = self.from_u
        """
        # push this transaction to reject state
        if self.state == PREPARE:
            self.state = REJECTED
            self.save()
            return True
        return False

    def force_reject(self):
        """
        @pre: request_user = self.owner
        """
        if self.state == PREPARE or self.state == APPROVED:
            self.state = REJECTED
            self.save()
            return True
        return False

    def resume(self):
        if self.state in [REJECTED, SUSPEND]:
            self.state = PREPARE
            self.save()
            return True
        return False

    def commit(self):
        """
        @pre self.state == CONCENCUS
        """
        self.state = COMMITED
        self.save()

    def revoke(self):
        """
        @pre self.state == COMMITED
        """
        self.state = CONCENCUS
        self.save()

    def __str__(self):
        return "From %s to %s $%f in bill %d with state %s" % \
            (self.from_u.username, self.to_u.username,
             self.amount, self.bill.id, self.state)

    @classmethod
    def get_balance(cls, user, settle=None):
        """
        @post: settle== None ==> return == balance with all unfinished and concus bill
        @post: settle!= None ==>
            return == balance with all unfinished and concus bill
            and bill == settle
        """
        # construct the basic query
        query = cls.objects
        if settle != None:
            # update the query and filter
            # filter by the given settle
            query = query.filter(bill__settlement=settle).filter(
                Q(state=COMMITED) | Q(state=FINISH))

        else:
            query = query.filter(
                Q(state=COMMITED) | Q(state=CONCENCUS))

        # calculate the given user's balance
        # Coalesce is to provide a 0 when the none type is occour
        return query.filter(to_u=user).distinct().aggregate(
            asum=Coalesce(Sum('amount'), Value(0))
        )['asum']\
            - query.filter(from_u=user).distinct().aggregate(
            asum=Coalesce(Sum('amount'), Value(0))
        )['asum']

    @classmethod
    def get_gdp_by_settlement(cls, settle):
        return cls.get_trs_by_settlement(settle).aggregate(
            asum=Coalesce(Sum('amount'), Value(0))
        )['asum']

    @classmethod
    def get_trs_by_settlement(cls, settle):
        return cls.objects.filter(bill__settlement=settle)

    @classmethod
    def filter_user(cls, user):
        return cls.objects.filter(
            Q(to_u=user) | Q(from_u=user)
        )

    @classmethod
    def get_waitng_tr(cls, user):
        """
        Find all the waiting decition trancation by this user
        """
        return cls.filter_user(user).filter(state=PREPARE)
コード例 #10
0
ファイル: models.py プロジェクト: ASHCO-2019/developer-portal
class Event(BasePage):
    resource_type = "event"
    parent_page_types = ["events.Events"]
    subpage_types = []
    template = "event.html"

    # Content fields
    description = RichTextField(
        blank=True,
        default="",
        features=RICH_TEXT_FEATURES_SIMPLE,
        help_text="Optional short text description, max. 400 characters",
        max_length=400,
    )
    image = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
    )
    body = CustomStreamField(
        blank=True,
        null=True,
        help_text=(
            "Optional body content. Supports rich text, images, embed via URL, "
            "embed via HTML, and inline code snippets"
        ),
    )
    agenda = StreamField(
        StreamBlock([("agenda_item", AgendaItemBlock())], required=False),
        blank=True,
        null=True,
        help_text="Optional list of agenda items for this event",
    )
    speakers = StreamField(
        StreamBlock(
            [
                ("speaker", PageChooserBlock(target_model="people.Person")),
                ("external_speaker", ExternalSpeakerBlock()),
            ],
            required=False,
        ),
        blank=True,
        null=True,
        help_text="Optional list of speakers for this event",
    )

    # Card fields
    card_title = CharField("Title", max_length=140, blank=True, default="")
    card_description = TextField("Description", max_length=400, blank=True, default="")
    card_image = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
        verbose_name="Image",
    )

    # Meta fields
    start_date = DateField(default=datetime.date.today)
    end_date = DateField(blank=True, null=True)
    latitude = FloatField(blank=True, null=True)
    longitude = FloatField(blank=True, null=True)
    register_url = URLField("Register URL", blank=True, null=True)
    venue_name = CharField(max_length=100, blank=True, default="")
    venue_url = URLField("Venue URL", max_length=100, blank=True, default="")
    address_line_1 = CharField(max_length=100, blank=True, default="")
    address_line_2 = CharField(max_length=100, blank=True, default="")
    address_line_3 = CharField(max_length=100, blank=True, default="")
    city = CharField(max_length=100, blank=True, default="")
    state = CharField("State/Province/Region", max_length=100, blank=True, default="")
    zip_code = CharField("Zip/Postal code", max_length=100, blank=True, default="")
    country = CountryField(blank=True, default="")
    keywords = ClusterTaggableManager(through=EventTag, blank=True)

    # Content panels
    content_panels = BasePage.content_panels + [
        FieldPanel("description"),
        MultiFieldPanel(
            [ImageChooserPanel("image")],
            heading="Image",
            help_text=(
                "Optional header image. If not specified a fallback will be used. "
                "This image is also shown when sharing this page via social media"
            ),
        ),
        StreamFieldPanel("body"),
        StreamFieldPanel("agenda"),
        StreamFieldPanel("speakers"),
    ]

    # Card panels
    card_panels = [
        FieldPanel("card_title"),
        FieldPanel("card_description"),
        ImageChooserPanel("card_image"),
    ]

    # Meta panels
    meta_panels = [
        MultiFieldPanel(
            [
                FieldPanel("start_date"),
                FieldPanel("end_date"),
                FieldPanel("latitude"),
                FieldPanel("longitude"),
                FieldPanel("register_url"),
            ],
            heading="Event details",
            classname="collapsible",
            help_text=mark_safe(
                "Optional time and location information for this event. Latitude and "
                "longitude are used to show a map of the event’s location. For more "
                "information on finding these values for a given location, "
                "'<a href='https://support.google.com/maps/answer/18539'>"
                "see this article</a>"
            ),
        ),
        MultiFieldPanel(
            [
                FieldPanel("venue_name"),
                FieldPanel("venue_url"),
                FieldPanel("address_line_1"),
                FieldPanel("address_line_2"),
                FieldPanel("address_line_3"),
                FieldPanel("city"),
                FieldPanel("state"),
                FieldPanel("zip_code"),
                FieldPanel("country"),
            ],
            heading="Event address",
            classname="collapsible",
            help_text=(
                "Optional address fields. The city and country are also shown "
                "on event cards"
            ),
        ),
        MultiFieldPanel(
            [InlinePanel("topics")],
            heading="Topics",
            help_text=(
                "These are the topic pages the event will appear on. The first topic "
                "in the list will be treated as the primary topic and will be shown "
                "in the page’s related content."
            ),
        ),
        MultiFieldPanel(
            [
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("social_image"),
                FieldPanel("keywords"),
            ],
            heading="SEO",
            help_text=(
                "Optional fields to override the default title and description "
                "for SEO purposes"
            ),
        ),
    ]

    # Settings panels
    settings_panels = [FieldPanel("slug")]

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

    @property
    def is_upcoming(self):
        """Returns whether an event is in the future."""
        return self.start_date > datetime.date.today()

    @property
    def primary_topic(self):
        """Return the first (primary) topic specified for the event."""
        article_topic = self.topics.first()
        return article_topic.topic if article_topic else None

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

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

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

    @property
    def event_dates_full(self):
        """Return a formatted string of the event start and end dates,
        including the year"""
        return self.event_dates + self.start_date.strftime(", %Y")

    def has_speaker(self, person):
        for speaker in self.speakers:  # pylint: disable=not-an-iterable
            if speaker.block_type == "speaker" and str(speaker.value) == str(
                person.title
            ):
                return True
        return False
コード例 #11
0
ファイル: models.py プロジェクト: ASHCO-2019/developer-portal
class EventTopic(Orderable):
    event = ParentalKey("Event", related_name="topics")
    topic = ForeignKey("topics.Topic", on_delete=CASCADE, related_name="+")
    panels = [PageChooserPanel("topic")]
コード例 #12
0
class ConversationParticipant(BaseModel):
    """The join table between Conversation and User."""
    user = ForeignKey(settings.AUTH_USER_MODEL)
    conversation = ForeignKey(Conversation)
コード例 #13
0
class FieldFromModelForeignKeyTest(Model):
    foo_fk = ForeignKey(Foo, on_delete=CASCADE)
コード例 #14
0
class Bar(Model):
    foo = ForeignKey(Foo, related_name='bars', on_delete=CASCADE, help_text='bar_help_text')
コード例 #15
0
class BillingDocumentBase(models.Model):
    objects = BillingDocumentManager.from_queryset(BillingDocumentQuerySet)()

    class STATES(object):
        DRAFT = 'draft'
        ISSUED = 'issued'
        PAID = 'paid'
        CANCELED = 'canceled'

    STATE_CHOICES = Choices(
        (STATES.DRAFT, _('Draft')), (STATES.ISSUED, _('Issued')),
        (STATES.PAID, _('Paid')), (STATES.CANCELED, _('Canceled')))

    series = models.CharField(max_length=20,
                              blank=True,
                              null=True,
                              db_index=True)
    number = models.IntegerField(blank=True, null=True, db_index=True)
    customer = models.ForeignKey('Customer')
    provider = models.ForeignKey('Provider')
    archived_customer = JSONField(default=dict, null=True, blank=True)
    archived_provider = JSONField(default=dict, null=True, blank=True)
    due_date = models.DateField(null=True, blank=True)
    issue_date = models.DateField(null=True, blank=True, db_index=True)
    paid_date = models.DateField(null=True, blank=True)
    cancel_date = models.DateField(null=True, blank=True)
    sales_tax_percent = models.DecimalField(
        max_digits=4,
        decimal_places=2,
        validators=[MinValueValidator(0.0)],
        null=True,
        blank=True)
    sales_tax_name = models.CharField(max_length=64, blank=True, null=True)
    currency = models.CharField(choices=currencies,
                                max_length=4,
                                default='USD',
                                help_text='The currency used for billing.')
    transaction_currency = models.CharField(
        choices=currencies,
        max_length=4,
        help_text='The currency used when making a transaction.')
    transaction_xe_rate = models.DecimalField(
        max_digits=16,
        decimal_places=4,
        null=True,
        blank=True,
        help_text='Currency exchange rate from document currency to '
        'transaction_currency.')
    transaction_xe_date = models.DateField(
        null=True,
        blank=True,
        help_text='Date of the transaction exchange rate.')

    pdf = ForeignKey(PDF, null=True)
    state = FSMField(choices=STATE_CHOICES,
                     max_length=10,
                     default=STATES.DRAFT,
                     verbose_name="State",
                     help_text='The state the invoice is in.')

    _last_state = None

    class Meta:
        abstract = True
        unique_together = ('provider', 'series', 'number')
        ordering = ('-issue_date', 'series', '-number')

    def __init__(self, *args, **kwargs):
        super(BillingDocumentBase, self).__init__(*args, **kwargs)
        self._last_state = self.state

    def mark_for_generation(self):
        self.pdf.mark_as_dirty()

    def _issue(self, issue_date=None, due_date=None):
        if issue_date:
            self.issue_date = datetime.strptime(issue_date, '%Y-%m-%d').date()
        elif not self.issue_date and not issue_date:
            self.issue_date = timezone.now().date()

        if not self.transaction_xe_rate:
            if not self.transaction_xe_date:
                self.transaction_xe_date = self.issue_date

            try:
                xe_rate = CurrencyConverter.convert(1, self.currency,
                                                    self.transaction_currency,
                                                    self.transaction_xe_date)
            except RateNotFound:
                raise TransitionNotAllowed('Couldn\'t automatically obtain an '
                                           'exchange rate.')

            self.transaction_xe_rate = xe_rate

        if due_date:
            self.due_date = datetime.strptime(due_date, '%Y-%m-%d').date()
        elif not self.due_date and not due_date:
            delta = timedelta(days=PAYMENT_DUE_DAYS)
            self.due_date = timezone.now().date() + delta

        if not self.sales_tax_name:
            self.sales_tax_name = self.customer.sales_tax_name
        if not self.sales_tax_percent:
            self.sales_tax_percent = self.customer.sales_tax_percent

        if not self.number:
            self.number = self._generate_number()

        self.archived_customer = self.customer.get_archivable_field_values()

    @transition(field=state, source=STATES.DRAFT, target=STATES.ISSUED)
    def issue(self, issue_date=None, due_date=None):
        self._issue(issue_date=issue_date, due_date=due_date)

    def _pay(self, paid_date=None):
        if paid_date:
            self.paid_date = datetime.strptime(paid_date, '%Y-%m-%d').date()
        if not self.paid_date and not paid_date:
            self.paid_date = timezone.now().date()

    @transition(field=state, source=STATES.ISSUED, target=STATES.PAID)
    def pay(self, paid_date=None):
        self._pay(paid_date=paid_date)

    def _cancel(self, cancel_date=None):
        if cancel_date:
            self.cancel_date = datetime.strptime(cancel_date,
                                                 '%Y-%m-%d').date()
        if not self.cancel_date and not cancel_date:
            self.cancel_date = timezone.now().date()

    @transition(field=state, source=STATES.ISSUED, target=STATES.CANCELED)
    def cancel(self, cancel_date=None):
        self._cancel(cancel_date=cancel_date)

    def sync_related_document_state(self):
        if self.related_document and self.state != self.related_document.state:
            state_transition_map = {
                BillingDocumentBase.STATES.ISSUED: 'issue',
                BillingDocumentBase.STATES.CANCELED: 'cancel',
                BillingDocumentBase.STATES.PAID: 'pay'
            }
            transition_name = state_transition_map[self.state]

            bound_transition_method = getattr(self.related_document,
                                              transition_name)
            bound_transition_method()

    def clone_into_draft(self):
        copied_fields = {
            'customer': self.customer,
            'provider': self.provider,
            'currency': self.currency,
            'sales_tax_percent': self.sales_tax_percent,
            'sales_tax_name': self.sales_tax_name
        }

        clone = self.__class__._default_manager.create(**copied_fields)
        clone.state = self.STATES.DRAFT

        # clone entries too
        for entry in self._entries:
            entry_clone = entry.clone()
            document_type_name = self.__class__.__name__.lower()
            setattr(entry_clone, document_type_name, clone)
            entry_clone.save()

        clone.save()

        return clone

    def clean(self):
        super(BillingDocumentBase, self).clean()

        # The only change that is allowed if the document is in issued state
        # is the state chage from issued to paid
        # !! TODO: If _last_state == 'issued' and self.state == 'paid' || 'canceled'
        # it should also be checked that the other fields are the same bc.
        # right now a document can be in issued state and someone could
        # send a request which contains the state = 'paid' and also send
        # other changed fields and the request would be accepted bc. only
        # the state is verified.
        if self._last_state == self.STATES.ISSUED and\
           self.state not in [self.STATES.PAID, self.STATES.CANCELED]:
            msg = 'You cannot edit the document once it is in issued state.'
            raise ValidationError({NON_FIELD_ERRORS: msg})

        if self._last_state == self.STATES.CANCELED:
            msg = 'You cannot edit the document once it is in canceled state.'
            raise ValidationError({NON_FIELD_ERRORS: msg})

        # If it's in paid state => don't allow any changes
        if self._last_state == self.STATES.PAID:
            msg = 'You cannot edit the document once it is in paid state.'
            raise ValidationError({NON_FIELD_ERRORS: msg})

        if self.transactions.exclude(
                currency=self.transaction_currency).exists():
            message = 'There are unfinished transactions of this document that use a ' \
                      'different currency.'
            raise ValidationError({'transaction_currency': message})

    def save(self, *args, **kwargs):
        if not self.transaction_currency:
            self.transaction_currency = self.customer.currency or self.currency

        if not self.series:
            self.series = self.default_series

        # Generate the number
        if not self.number and self.state != BillingDocumentBase.STATES.DRAFT:
            self.number = self._generate_number()

        # Add tax info
        if not self.sales_tax_name:
            self.sales_tax_name = self.customer.sales_tax_name
        if not self.sales_tax_percent:
            self.sales_tax_percent = self.customer.sales_tax_percent

        self._last_state = self.state

        with db_transaction.atomic():
            # Create pdf object
            if not self.pdf and self.state != self.STATES.DRAFT:
                self.pdf = PDF.objects.create(
                    upload_path=self.get_pdf_upload_path(), dirty=1)

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

    def _generate_number(self, default_starting_number=1):
        """Generates the number for a proforma/invoice."""
        default_starting_number = max(default_starting_number, 1)

        documents = self.__class__._default_manager.filter(
            provider=self.provider, series=self.series)
        if not documents.exists():
            # An invoice/proforma with this provider and series does not exist
            if self.series == self.default_series:
                return self._starting_number
            else:
                return default_starting_number
        else:
            # An invoice with this provider and series already exists
            max_existing_number = documents.aggregate(
                Max('number'))['number__max']
            if max_existing_number:
                if self._starting_number and self.series == self.default_series:
                    return max(max_existing_number + 1, self._starting_number)
                else:
                    return max_existing_number + 1
            else:
                return default_starting_number

    def series_number(self):
        if self.series:
            if self.number:
                return "%s-%d" % (self.series, self.number)
            else:
                return "%s-draft-id:%d" % (self.series, self.pk)

        else:
            return "draft-id:%d" % self.pk

    series_number.short_description = 'Number'
    series_number = property(series_number)

    def __unicode__(self):
        return u'%s %s => %s [%.2f %s]' % (
            self.series_number, self.provider.billing_name,
            self.customer.billing_name, self.total, self.currency)

    @property
    def updateable_fields(self):
        return [
            'customer', 'provider', 'due_date', 'issue_date', 'paid_date',
            'cancel_date', 'sales_tax_percent', 'sales_tax_name', 'currency'
        ]

    @property
    def admin_change_url(self):
        url_base = 'admin:{app_label}_{klass}_change'.format(
            app_label=self._meta.app_label,
            klass=self.__class__.__name__.lower())
        url = reverse(url_base, args=(self.pk, ))
        return '<a href="{url}">{display_series}</a>'.format(
            url=url, display_series=self.series_number)

    @property
    def _entries(self):
        # entries iterator which replaces the invoice/proforma from the DB with
        # self. We need this in generate_pdf so that the data in PDF has the
        # lastest state for the document. Without this we get in template:
        #
        # invoice.issue_date != entry.invoice.issue_date
        #
        # which is obviously false.
        document_type_name = self.__class__.__name__  # Invoice or Proforma
        kwargs = {document_type_name.lower(): self}
        entries = DocumentEntry.objects.filter(**kwargs)
        for entry in entries:
            if document_type_name.lower() == 'invoice':
                entry.invoice = self
            if document_type_name.lower() == 'proforma':
                entry.proforma = self
            yield (entry)

    @property
    def transactions(self):
        return self.transaction_set.all()

    def get_template_context(self, state=None):
        customer = Customer(**self.archived_customer)
        provider = Provider(**self.archived_provider)
        if state is None:
            state = self.state

        return {
            'document': self,
            'provider': provider,
            'customer': customer,
            'entries': self._entries,
            'state': state
        }

    def get_template(self, state=None):
        provider_state_template = '{provider}/{kind}_{state}_pdf.html'.format(
            kind=self.kind, provider=self.provider.slug, state=state).lower()
        provider_template = '{provider}/{kind}_pdf.html'.format(
            kind=self.kind, provider=self.provider.slug).lower()
        generic_state_template = '{kind}_{state}_pdf.html'.format(
            kind=self.kind, state=state).lower()
        generic_template = '{kind}_pdf.html'.format(kind=self.kind).lower()
        _templates = [
            provider_state_template, provider_template, generic_state_template,
            generic_template
        ]

        templates = []
        for t in _templates:
            templates.append('billing_documents/' + t)

        return select_template(templates)

    def get_pdf_filename(self):
        return '{doc_type}_{series}-{number}.pdf'.format(
            doc_type=self.__class__.__name__,
            series=self.series,
            number=self.number)

    def get_pdf_upload_path(self):
        path_template = getattr(
            settings, 'SILVER_DOCUMENT_UPLOAD_PATH',
            'documents/{provider}/{doc.kind}/{issue_date}/{filename}')

        context = {
            'doc': self,
            'filename': self.get_pdf_filename(),
            'provider': self.provider.slug,
            'customer': self.customer.slug,
            'issue_date': self.issue_date.strftime('%Y/%m/%d')
        }

        return path_template.format(**context)

    def generate_pdf(self, state=None, upload=True):
        # !!! ensure this is not called concurrently for the same document

        context = self.get_template_context(state)
        context['filename'] = self.get_pdf_filename()

        pdf_file_object = self.pdf.generate(template=self.get_template(state),
                                            context=context,
                                            upload=upload)

        return pdf_file_object

    def generate_html(self, state=None, request=None):
        context = self.get_template_context(state)
        template = self.get_template(state=context['state'])

        return template.render(context, request)

    def serialize_hook(self, hook):
        """
        Used to generate a skinny payload.
        """

        return {'hook': hook.dict(), 'data': {'id': self.id}}

    @property
    def entries(self):
        raise NotImplementedError

    @property
    def total(self):
        return sum([entry.total for entry in self.entries])

    @property
    def total_before_tax(self):
        return sum([entry.total_before_tax for entry in self.entries])

    @property
    def tax_value(self):
        return sum([entry.tax_value for entry in self.entries])

    @property
    def total_in_transaction_currency(self):
        return sum(
            [entry.total_in_transaction_currency for entry in self.entries])

    @property
    def total_before_tax_in_transaction_currency(self):
        return sum([
            entry.total_before_tax_in_transaction_currency
            for entry in self.entries
        ])

    @property
    def tax_value_in_transaction_currency(self):
        return sum([
            entry.tax_value_in_transaction_currency for entry in self.entries
        ])

    @property
    def amount_paid_in_transaction_currency(self):
        Transaction = apps.get_model('silver.Transaction')

        return sum([
            transaction.amount for transaction in self.transactions.filter(
                state=Transaction.States.Settled)
        ])

    @property
    def amount_pending_in_transaction_currency(self):
        Transaction = apps.get_model('silver.Transaction')

        return sum([
            transaction.amount for transaction in self.transactions.filter(
                state=Transaction.States.Pending)
        ])

    @property
    def amount_to_be_charged_in_transaction_currency(self):
        Transaction = apps.get_model('silver.Transaction')

        return self.total_in_transaction_currency - sum([
            transaction.amount
            for transaction in self.transactions.filter(state__in=[
                Transaction.States.Initial, Transaction.States.Pending,
                Transaction.States.Settled
            ])
        ])
コード例 #16
0
class Settlement(TimestampModel):
    """

    Settle transaction is created when the settlement class gain the lock
    The lock is provided by wait_count, when wait_count count the waiting bill
    is become 0, this settlement gain the lock.

    Then it will call the method at the end of this file, to creat all the settle
    transactions.
    """
    title = CharField(max_length=255)
    description = CharField(max_length=2048, blank=True)
    owner = ForeignKey(User, on_delete=models.PROTECT)
    group = ForeignKey(BillGroups, on_delete=models.PROTECT)
    # counter of unfinished integer
    # first save will not trigger s_tr creation
    # after it calculate how many bills it should wait
    wait_count = PositiveIntegerField(default=1)

    @property
    def state(self):
        # before the s_tr is set up, all is suspend
        if self.settletransaction_set.count() == 0:
            return SUSPEND
        # maping of the state from transaction
        s = {
            PREPARE: PREPARE,
            APPROVED: PREPARE,
            REJECTED: SUSPEND,  # Not exist
            CONCENCUS: PREPARE,
            COMMITED: FINISH,  # Not exist
            FINISH: FINISH,
            SUSPEND: PREPARE,  # Not exist in mapping
            None: PREPARE
        }
        # get random one of tr
        # and return its mapping
        tr_first = self.settletransaction_set.first()
        if tr_first:
            return s[tr_first.state]
        return PREPARE

    @property
    def gdp(self):
        return Transaction.get_gdp_by_settlement(self)

    @property
    def tr_count(self):
        return Transaction.get_trs_by_settlement(self).count()

    def get_balance_by_user(self, user):
        """ 
        Use settle transaction to trace back the balance
        to reduce the calculation from server  
        """

        query = self.settletransaction_set
        return query.filter(to_u=user).distinct().aggregate(
            asum=Coalesce(Sum('amount'), Value(0))
        )['asum']\
            - query.filter(from_u=user).distinct().aggregate(
            asum=Coalesce(Sum('amount'), Value(0))
        )['asum']

    def get_actual_pay_by_user(self, user):
        return Transaction.get_trs_by_settlement(self)\
            .filter(from_u=user).aggregate(
                asum=Coalesce(Sum('amount'), Value(0))
        )['asum']

    def try_finish(self):
        """
        When all the s_tr is concencus, finish all the tr,
        and finish all the bills
        """

        if self.settletransaction_set.exclude(
                state=CONCENCUS).count() == 0:
            # there's no more s_tr to wait, end this s_tr and
            # finish all the bills
            self.settletransaction_set.filter(
                state=CONCENCUS).update(state=FINISH)
            Transaction.objects.filter(
                state=COMMITED,
                bill__settlement__id=self.id
            ).update(state=FINISH)

    def get_waiting_bill(self):
        """
        return:list<bill>
        @post:  forall bill in return ==> bill.state != COMMITED && bill.state != FINISHED
        """
        return Bill.objects\
            .exclude(transaction__state=COMMITED)\
            .exclude(transaction__state=FINISH)\
            .filter(settlement=self)\
            .distinct()
コード例 #17
0
ファイル: Equipment.py プロジェクト: xhackax47/AXEL_WEB
class Equipement(Model):
    armure = ForeignKey(Armure, on_delete=models.CASCADE, default=None)
    armes = ForeignKey(Arme, on_delete=models.CASCADE, default=None)
    bourse = ForeignKey(Bourse, on_delete=models.CASCADE, default=None)
    objets = ForeignKey(Objet, on_delete=models.CASCADE, default=None)
    outils = ForeignKey(Outil, on_delete=models.CASCADE, default=None)
コード例 #18
0
class Bill(TimestampModel):
    # informaqtion about this bill
    title = CharField(max_length=255)
    description = CharField(max_length=2048, blank=True)
    # creator of this bill
    owner = ForeignKey(User, on_delete=models.PROTECT)
    group = ForeignKey(BillGroups, on_delete=models.PROTECT)

    """
    Settlement will be attach to a bill
    Nullable foreign key will require both nullable and blankable
    https://stackoverflow.com/questions/16589069/foreignkey-does-not-allow-null-values
    Blankable is for validator, nullable is for database creation
    """
    settlement = ForeignKey(
        'Settlement',
        on_delete=models.SET_NULL,
        null=True,
        blank=True
    )

    @property
    def state(self):
        # maping of the state from transaction
        s = {
            PREPARE: PREPARE,
            APPROVED: PREPARE,
            REJECTED: SUSPEND,
            CONCENCUS: CONCENCUS,
            COMMITED: COMMITED,
            FINISH: FINISH,
            SUSPEND: SUSPEND,
            None: SUSPEND
        }
        # get random one of tr
        # and return its mapping
        tr_first = self.transaction_set.first()
        if tr_first:
            return s[tr_first.state]
        return PREPARE

    def tr_state_update(self, request_uesr, to_state):
        """
        @request_user: who make this request
        @to_state: which state is this request is make to

        @pre: tr update is successful

        @post: if forall tr.state == APPROVE and \
                tr.bill.settlement == NULL
                then all tr.state == CONCENCUS
        @post: if forall tr.state == APPROVE and \
                tr.bill.settlement != NULL
                then all tr.state == COMMITED
        @post: if to_state == REJECTED and \
                then for all tr.state != REJECTED, tr.state = REJECTED and
                this.exclude(tr__state = REJECTED).count() == 1
        """
        # filter out the transaction set might need to update
        trs = self.transaction_set.all()

        if to_state == APPROVED:
            if trs.exclude(state=APPROVED).count() == 0:

                # decrease the lock of settlement
                # if there's any
                if self.settlement != None:
                    # if the settelment is attached,
                    # then the state is directly to COMMITED
                    trs.update(state=COMMITED)
                    assert(trs.count != 0)

                    # then here will infrom the settlement to settup the
                    # settlement transactions
                    self.settlement.wait_count -= 1
                    self.settlement.save()

                    return

                # ELSE
                # all transations are approved
                # push to next stage for all the transactions
                trs.update(state=CONCENCUS)

            else:
                # need more waiting
                return
        elif to_state == REJECTED:
            # push all the bill to suspend state
            for tr in trs.exclude(from_u=request_uesr):
                tr.state = SUSPEND
                tr.save()

    @classmethod
    def approve_all(cls, request_user):
        # find all the pending bill he need to pay,
        # then pay them all
        bills = cls.objects\
            .filter(
                transaction__state=PREPARE,
                transaction__from_u=request_user
            )

        """
        O(n^2) operation, no way to get faster
        """
        for b in bills:
            b.approve(request_user)

    @classmethod
    def filter_user_bills(cls, user):
        return cls.objects.filter(
            Q(owner=user) |
            Q(transaction__from_u=user) |
            Q(transaction__to_u=user)
        ).distinct()

    @classmethod
    def get_process(cls, user):
        return cls.filter_user_bills(user).filter(
            Q(transaction__state=PREPARE) |
            Q(transaction__state=APPROVED) |
            Q(transaction__state=SUSPEND) |
            Q(transaction__state=REJECTED)
        ).distinct()

    def approve(self, request_uesr):
        # the transactions need to approved by request user

        if self.transaction_set.get(from_u=request_uesr).approve():
            self.tr_state_update(request_uesr, APPROVED)

    def reject(self, request_uesr):
        myTr = self.transaction_set.get(
            from_u=request_uesr
        )
        if request_uesr == self.owner:
            print("force reject now")
            ret = myTr.force_reject()
        else:
            ret = myTr.reject()
        if ret:
            self.tr_state_update(request_uesr, REJECTED)

    def resume(self, request_user):
        if request_user == self.owner:
            for tr in self.transaction_set.all():
                tr.resume()
                if tr.from_u == request_user:
                    tr.approve()
            return True
        return False

    # def suspend(self, request_user):

    def commit(self):
        """
        Non 'public' method, it's called by Settlement class
        """
        if self.state == CONCENCUS:
            for tr in self.transaction_set.all():
                # update all it's transaction to commit
                tr.commit()
            return True
        return False

    def revoke(self):
        if self.state == COMMITED:
            for tr in self.transaction_set.all():
                # update all it's transaction to CONCENCUS
                tr.revoke()
            return True
        return False
コード例 #19
0
ファイル: models.py プロジェクト: gonczor/black-sheep-codes
class TestQuestion(Model):
    test = ForeignKey("Test", on_delete=CASCADE, related_name="questions")
    text = TextField()
コード例 #20
0
class LocationCapability(Model):
    location = ForeignKey(Location, null=True, on_delete=CASCADE)
    capability = CharField(max_length=50,
                           choices=SQUARE_CAPABILITIES,
                           default="CREDIT_CARD_PROCESSING")
コード例 #21
0
ファイル: models.py プロジェクト: CyberLight/silk
class SQLQuery(models.Model):
    query = TextField()
    start_time = DateTimeField(null=True, blank=True, default=timezone.now)
    end_time = DateTimeField(null=True, blank=True)
    time_taken = FloatField(blank=True, null=True)
    request = ForeignKey('Request',
                         related_name='queries',
                         null=True,
                         blank=True,
                         db_index=True)
    traceback = TextField()
    objects = SQLQueryManager()

    # TODO: Document SILKY_PROJECT_ROOT_DIR and SILKY_PROJECT_EXCLUDE_DIRS
    project_dir = getattr(settings, 'SILKY_PROJECT_ROOT_DIR', None) or ''
    exclude_dirs = getattr(settings, 'SILKY_PROJECT_EXCLUDE_DIRS', None) or []
    if project_dir and project_dir[-1] != os.sep:
        project_dir += os.sep
    traceback_pattern = re.compile(
        r'File "%s(?P<file>.*)", line (?P<line>\d+), in (?P<method>.*)' %
        re.escape(project_dir))
    exclude_patterns = [
        re.compile(r'File ".*silk%ssql.*", line \d+, in .*' % os.sep),
        re.compile(r'File ".*django%sdb%smodels.*", line \d+, in .*' %
                   (os.sep, os.sep)),
    ]
    for exclude_dir in exclude_dirs:
        if exclude_dir[-1] != os.sep:
            exclude_dir += os.sep
        exclude_patterns.append(
            re.compile(r'File "%s.*", line \d+, in .*' %
                       re.escape(exclude_dir)))

    def match_tb(self, tb):
        tb = tb.strip()
        match = self.traceback_pattern.match(tb)
        for exclude_pattern in self.exclude_patterns:
            if not match:
                break
            if exclude_pattern.match(tb):
                match = False
        return match

    @property
    def traceback_ln_only(self):
        return '\n'.join(self.traceback.split('\n')[::2])

    @property
    def traceback_ln_only_with_highlights(self):
        return [(tb, bool(self.match_tb(tb)))
                for tb in self.traceback.split('\n')[::2]]

    @property
    def formatted_query(self):
        return sqlparse.format(self.query, reindent=True, keyword_case='upper')

    # TODO: Surely a better way to handle this? May return false positives
    @property
    def num_joins(self):
        return self.query.lower().count('join ')

    @property
    def tables_involved(self):
        """A really rather rudimentary way to work out tables involved in a query.
        TODO: Can probably parse the SQL using sqlparse etc and pull out table info that way?"""
        components = [x.strip() for x in self.query.split()]
        tables = []
        for idx, c in enumerate(components):
            # TODO: If django uses aliases on column names they will be falsely identified as tables...
            if c.lower() in ['from', 'join', 'as', 'into', 'update']:
                try:
                    nxt = components[idx + 1]
                    if nxt.startswith('('):  # Subquery
                        continue
                    if nxt.startswith('@'):  # Temporary table
                        continue
                    stripped = nxt.strip().strip(',')
                    if stripped:
                        tables.append(stripped)
                except IndexError:  # Reach the end
                    pass
        return tables

    @property
    def last_project_method(self):
        for tb in self.traceback.split('\n')[::2]:
            match = self.match_tb(tb)
            if match:
                return "%s in %s:%s" % (match.group('method'),
                                        match.group('file'),
                                        match.group('line'))
        return ""

    def calculate_time_taken(self):
        if self.end_time and self.start_time:
            interval = self.end_time - self.start_time
            self.time_taken = interval.total_seconds() * 1000

    @transaction.commit_on_success()
    def save(self, *args, **kwargs):
        self.calculate_time_taken()
        super(SQLQuery, self).save(*args, **kwargs)

    @transaction.commit_on_success()
    def delete(self, *args, **kwargs):
        self.request.num_sql_queries -= 1
        self.request.save()
        super(SQLQuery, self).delete(*args, **kwargs)
コード例 #22
0
class Catalog(Model):
    location = ForeignKey(Location, null=True, on_delete=SET_NULL)
    name = CharField(max_length=255, blank=True, null=True)
    version = BigIntegerField(blank=True, null=True)

    def __str__(self):
        retn = self.name

        if self.location is not None:
            retn = "{} at {}".format(self.name, self.location)

        return retn

    def list(self):
        if self.version is not None:
            result = square_client.catalog.list_catalog(types="ITEM",
                                                        version=self.version)
        else:
            result = square_client.catalog.list_catalog(types="ITEM")
        cat = result.body.get(
            "objects") if result.is_success() else result.errors

        for item in cat:
            object_id = item.get("id")
            item_data = item.get("item_data")
            self.version = item.get("version")
            try:
                catalog_item = CatalogItem.objects.get(object_id=object_id)
            except CatalogItem.DoesNotExist:
                catalog_item = CatalogItem.objects.create(
                    catalog=self,
                    name=item_data.get("name"),
                    description=item_data.get("description"),
                    item_abbreviation=item_data.get("abbreviation"),
                    object_id=object_id,
                )
            else:
                catalog_item.name = item_data.get("name")
                catalog_item.description = item_data.get("description")
                catalog_item.item_abbreviation = item_data.get("abbreviation")
                catalog_item.save()

            for variation in item_data.get("variations"):
                v_object_id = variation.get("id")
                v_item_data = variation.get("item_variation_data")
                try:
                    variant = CatalogVariant.objects.get(object_id=v_object_id)
                except CatalogVariant.DoesNotExist:
                    variant = CatalogVariant.objects.create(
                        catalog_item=catalog_item,
                        name=v_item_data.get("name"),
                        price=v_item_data.get("price_money").get("amount"),
                        object_id=v_object_id,
                    )
                else:
                    variant.catalog_item = catalog_item
                    variant.name = v_item_data.get("name")
                    variant.price = v_item_data.get("price_money").get(
                        "amount")
                    variant.object_id = v_object_id
                    variant.save()

                catalog_item.save()

        self.save()

        return cat
コード例 #23
0
ファイル: models.py プロジェクト: roodie-ree/cookcook
class Ingredient(Model):
    recipe = ForeignKey(Recipe, on_delete=CASCADE)
    food = ForeignKey(Food, on_delete=PROTECT)
    amount = DecimalField(max_digits=5, decimal_places=2)
コード例 #24
0
class CatalogItem(Model):
    catalog = ForeignKey(Catalog, blank=True, null=True, on_delete=CASCADE)
    name = CharField(max_length=255, blank=True, null=True)
    description = TextField(blank=True, null=True)
    item_abbreviation = CharField(max_length=255, blank=True, null=True)
    item_key = UUIDField(blank=True, null=True)
    item_id = CharField(max_length=255, blank=True, null=True)
    object_id = CharField(max_length=255, blank=True, null=True)

    def __str__(self):
        return self.name

    def update_id_mappings(self, id_mappings=[]):
        for id_mapping in id_mappings:
            object_id = id_mapping.get("client_object_id ")
            try:
                variant = CatalogVariant.objects.get(catalog_item=self,
                                                     variant_key=object_id)
            except CatalogVariant.DoesNotExist:
                pass
            else:
                variant.object_id = object_id
                variant.save_id()

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

        result = square_client.catalog.delete_catalog_object(
            object_id=self.object_id)
        response = result.body if result.is_success() else result.errors
        log_message(response, pretty=True)

    def save(self,
             force_insert=False,
             force_update=False,
             using=None,
             update_fields=None):
        if not self.item_abbreviation or self.item_abbreviation is None:
            self.item_abbreviation = generate_abbreviation(self.name)

        if not self.item_key or self.item_key is None:
            self.item_key = str(uuid0.generate())

        if not self.item_id:
            self.item_id = "#{}".format(slugify(self.name))
            self.object_id = "#{}".format(slugify(self.name))

        super().save(force_insert, force_update, using, update_fields)

        itemid = self.item_id
        if not self._state.adding:
            if self.object_id is not None:
                itemid = self.object_id

        body = self.as_dict(itemid)

        log_message(body, pretty=True)

        if self.variants.count() > 0:
            result = square_client.catalog.upsert_catalog_object(body=body)
            catalog_item = result.body if result.is_success(
            ) else result.errors

            if "catalog_object" in catalog_item:
                self.catalog.version = catalog_item.get("catalog_object").get(
                    "version")
                self.object_id = catalog_item.get("catalog_object").get("id")
                super().save(force_insert, force_update, using, update_fields)

            try:
                id_mappings = catalog_item.get("id_mappings", [])
            except AttributeError:
                log_message(catalog_item, pretty=True)
            else:
                self.update_id_mappings(id_mappings)

    @property
    def idempotency_key(self):
        return str(uuid0.generate())

    @property
    def variants(self):
        return CatalogVariant.objects.filter(catalog_item=self)

    @property
    def abbreviation(self):
        pieces = self.name.split()
        return self.name[0:2] if len(pieces) == 1 else "".join(
            [x[0] for x in pieces])

    def get_item(self):
        version = self.catalog.version
        if not version:
            result = square_client.catalog.retrieve_catalog_object(
                object_id=self.item_key)
        else:
            result = square_client.catalog.retrieve_catalog_object(
                object_id=self.item_key, catalog_version=version)

        item = result.body if result.is_success() else result.errors
        if "object" in item:
            item = item.get("object")

        return item

    def as_dict(self, item_id):
        variations = [variant.as_dict() for variant in self.variants]

        return {
            "idempotency_key": self.idempotency_key,
            "object": {
                "type": "ITEM",
                "id": item_id,
                "item_data": {
                    "name": self.name,
                    "description": self.description,
                    "abbreviation": self.item_abbreviation,
                    "variations": variations,
                },
            },
        }
コード例 #25
0
class Entry(models.Model):

    TYPE_TUTORIAL = 'tutorial'
    TYPE_PROJECT = 'project'
    TYPE_JUPYTER = 'jupyter'

    TYPE_CHOICES = (
        (TYPE_TUTORIAL, 'Tutorial'),
        (TYPE_PROJECT, 'Project'),
        (TYPE_JUPYTER, 'Jupyter Notebook')
    )

    ICON_MAP = {
        TYPE_TUTORIAL: '<i class="fa fa-graduation-cap"></i>',
        TYPE_PROJECT: '<i class="fa fa-bolt"></i>',
        TYPE_JUPYTER: '<i class="fa fa-code"></i>',
    }

    type = CharField(max_length=10,
                     choices=TYPE_CHOICES)
    title = CharField(max_length=250,
                      help_text='The main title of the entry. Does not have to be unique')
    subtitle = CharField(max_length=250, default='', blank=True)
    description = CharField(max_length=200, default='', blank=True)
    author = ForeignKey(User, on_delete=models.CASCADE, default=1, related_name='tutorials')
    thumbnail = FilerImageField(related_name="tutorial_thumbnail", on_delete=models.CASCADE, null=True)

    slug = SlugField(max_length=250, default='auto', unique=True)

    content = TextField(default='')
    text = TextField(default='')

    publishing_date = DateTimeField(default=timezone.now)
    creation_date = DateTimeField(default=timezone.now)

    next = URLField(null=True, blank=True)
    previous = URLField(null=True, blank=True)

    comments = GenericRelation(Comment)

    def save(self, *args, **kwargs) -> None:
        if self.slug == 'auto':
            self.slug = slugify(self.title)

        # So we need the "text" property to literally just contain the content of the post in all lower case and
        # without all the html markup (this will be used for fulltext search of keywords). BeautifulSoup does this
        # really well natively.
        soup = BeautifulSoup(self.content, 'lxml')
        self.text = soup.text.lower()

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

    @classmethod
    def get_most_recent(cls, n: int) -> List[Entry]:

        entries = cls.objects.order_by('-publishing_date').exclude(publishing_date__gte=timezone.now()).all()[0:n]

        return entries

    @property
    def comment_count(self):
        return len(self.comments.filter(active=True))

    @property
    def fa_icon(self):
        return self.ICON_MAP[self.type]

    @property
    def url(self) -> str:
        """
        Returns the absolute URL of the detail view of the entry instance.

        :return:
        """
        # This is a bit hacky (because it technically stretches the limits of SOC a bit) but very convenient!
        # Here is the thing: This is an abstract base model which will be the base for multiple specific
        # implementations, but we want a common list view for those and a list view would need knowledge of the urls
        # of the detail pages to link to those correctly. Thus we need a generalized method for getting those aka
        # we cannot do the {% url "..." %} thing because for that we need to provide a *specific* model type, which we
        # generally do not know at that point since it could be any specific implementation. Sooo it would be better if
        # the url information for the detail page would be attached to every individual element right away <- this
        # property.
        # Now this only works because of an implicit assumption we make: In the "url.py" module we set the name of
        # the according detail view for a model to the "type" property of the corresponding model class. If we adhere
        # to this convention, we can make use of it here and already assemble all the information to let django reverse
        # the url for a specific Entry instance...
        view_name = f'{BlogAppConfig.name}:{self.type}'
        return reverse_lazy(view_name, kwargs={'slug': self.slug})

    def __str__(self):
        return f'{self.type.upper()}: {self.title}'
コード例 #26
0
class CatalogVariant(Model):
    catalog_item = ForeignKey(CatalogItem,
                              blank=True,
                              null=True,
                              on_delete=CASCADE)
    name = CharField(max_length=255, blank=True, null=True)
    item_id = UUIDField(blank=True, null=True)
    price = PositiveIntegerField(null=True)
    variant_key = CharField(max_length=255, blank=True, null=True)
    object_id = CharField(max_length=255, blank=True, null=True)
    rfid_id = CharField(max_length=255, blank=True, null=True)
    upc_id = CharField(max_length=255, blank=True, null=True)

    def __str__(self):
        return "{} variant of {}".format(self.name, self.catalog_item)

    def save_id(self,
                force_insert=False,
                force_update=False,
                using=None,
                update_fields=None):
        super().save(force_insert, force_update, using, update_fields)

    def save(self,
             force_insert=False,
             force_update=False,
             using=None,
             update_fields=None):
        if not self.item_id:
            self.item_id = str(uuid0.generate())

        if not self.variant_key:
            self.variant_key = "#{}".format(slugify(self.name))
            self.object_id = "#{}".format(slugify(self.name))

        super().save(force_insert, force_update, using, update_fields)

        if self.catalog_item is not None:
            self.catalog_item.save()

    def as_dict(self):
        item_id = None
        if self.catalog_item:
            if self.catalog_item.object_id is not None:
                item_id = self.catalog_item.object_id
            elif self.catalog_item.item_key:
                item_id = self.catalog_item.item_id

        return {
            "type": "ITEM_VARIATION",
            "id": self.object_id,
            "item_variation_data": {
                "item_id": item_id,
                "name": self.name,
                "pricing_type": "FIXED_PRICING",
                "price_money": {
                    "amount": self.price,
                    "currency": "USD"
                },
            },
        }
コード例 #27
0
class AppRelease(TranslatableModel):
    version = CharField(max_length=256, verbose_name=_('Version'),
                        help_text=_('Version follows Semantic Versioning'))
    app = ForeignKey('App', on_delete=CASCADE, verbose_name=_('App'),
                     related_name='releases')
    # dependencies
    php_extensions = ManyToManyField('PhpExtension', blank=True,
                                     through='PhpExtensionDependency',
                                     verbose_name=_(
                                         'PHP extension dependency'))
    databases = ManyToManyField('Database', blank=True,
                                through='DatabaseDependency',
                                verbose_name=_('Database dependency'))
    licenses = ManyToManyField('License', verbose_name=_('License'))
    shell_commands = ManyToManyField('ShellCommand', blank=True,
                                     verbose_name=_(
                                         'Shell command dependency'))
    php_version_spec = CharField(max_length=256,
                                 verbose_name=_('PHP version requirement'))
    platform_version_spec = CharField(max_length=256, verbose_name=_(
        'Platform version requirement'))
    raw_php_version_spec = CharField(max_length=256,
                                     verbose_name=_(
                                         'PHP version requirement (raw)'))
    raw_platform_version_spec = CharField(max_length=256, verbose_name=_(
        'Platform version requirement (raw)'))
    min_int_size = IntegerField(blank=True, default=32,
                                verbose_name=_('Minimum Integer bits'),
                                help_text=_('e.g. 32 for 32bit Integers'))
    download = URLField(max_length=256, blank=True,
                        verbose_name=_('Archive download URL'))
    created = DateTimeField(auto_now_add=True, editable=False,
                            verbose_name=_('Created at'))
    last_modified = DateTimeField(auto_now=True, editable=False, db_index=True,
                                  verbose_name=_('Updated at'))
    signature = TextField(verbose_name=_('Signature'), help_text=_(
        'A signature using SHA512 and the app\'s certificate'))
    translations = TranslatedFields(
        changelog=TextField(verbose_name=_('Changelog'), help_text=_(
            'The release changelog. Can contain Markdown'), default='')
    )
    is_nightly = BooleanField(verbose_name=_('Nightly'), default=False)

    class Meta:
        verbose_name = _('App release')
        verbose_name_plural = _('App releases')
        unique_together = (('app', 'version', 'is_nightly'),)
        ordering = ['-version']

    def can_update(self, user: User) -> bool:
        return self.app.owner == user or user in self.app.co_maintainers.all()

    def can_delete(self, user: User) -> bool:
        return self.can_update(user)

    def __str__(self) -> str:
        return '%s %s' % (self.app, self.version)

    def is_compatible(self, platform_version, inclusive=False):
        """Checks if a release is compatible with a platform version

        :param platform_version: the platform version, not required to be
                                 semver compatible
        :param inclusive: if True the check will also return True if an app
                          requires 9.0.1 and the given platform version is 9.0
        :return: True if compatible, otherwise false
        """

        min_version = Version(pad_min_version(platform_version))
        spec = Spec(self.platform_version_spec)
        if inclusive:
            max_version = Version(pad_max_inc_version(platform_version))
            return (min_version in spec or max_version in spec)
        else:
            return min_version in spec

    @property
    def is_unstable(self):
        return self.is_nightly or '-' in self.version
コード例 #28
0
class Order(Model):
    location = ForeignKey(Location, null=True, on_delete=CASCADE)
    order_id = CharField(max_length=255, blank=True, null=True)
    order_key = UUIDField(blank=True, null=True)

    @property
    def idempotency_key(self):
        return str(uuid0.generate())

    def save(self,
             force_insert=False,
             force_update=False,
             using=None,
             update_fields=None):
        super().save(force_insert, force_update, using, update_fields)

        if self._state.adding:
            result = square_client.orders.create_order(body=self.as_dict())
        else:
            result = square_client.orders.update_order(order_id=self.order_id,
                                                       body=self.update_dict())

        return result.is_success()

    def as_dict(self):
        return {
            "order": {
                "location_id":
                self.location.location_id,
                "reference_id":
                self.order_id,
                "line_items": [
                    item.as_dict()
                    for item in OrderItem.objects.filter(order=self)
                ],
                "taxes":
                [tax.as_dict() for tax in OrderTax.objects.filter(order=self)],
                "discounts": [
                    discount.as_dict()
                    for discount in OrderDiscount.objects.filter(order=self)
                ],
            },
            "idempotency_key": self.idempotency_key,
        }

    def update_dict(self):
        return {
            "order": {
                "location_id":
                self.location.location_id,
                "line_items": [
                    item.as_dict()
                    for item in OrderItem.objects.filter(order=self)
                ],
                "taxes":
                [tax.as_dict() for tax in OrderTax.objects.filter(order=self)],
                "discounts": [
                    discount.as_dict()
                    for discount in OrderDiscount.objects.filter(order=self)
                ],
                "version":
                1,
            },
            "fields_to_clear": ["line_items", "taxes", "discounts"],
            "idempotency_key": self.idempotency_key,
        }
コード例 #29
0
class IPRange(CleanSave, TimestampedModel):
    """Represents a range of IP addresses used for a particular purpose in
    MAAS, such as a DHCP range or a range of reserved addresses."""

    objects = IPRangeManager()

    subnet = ForeignKey('Subnet',
                        editable=True,
                        blank=False,
                        null=False,
                        on_delete=CASCADE)

    type = CharField(max_length=20,
                     editable=True,
                     choices=IPRANGE_TYPE_CHOICES,
                     null=False,
                     blank=False)

    start_ip = MAASIPAddressField(null=False,
                                  editable=True,
                                  blank=False,
                                  verbose_name='Start IP')

    end_ip = MAASIPAddressField(null=False,
                                editable=True,
                                blank=False,
                                verbose_name='End IP')

    user = ForeignKey(User,
                      default=None,
                      blank=True,
                      null=True,
                      editable=True,
                      on_delete=PROTECT)

    # In Django 1.8, CharFields with null=True, blank=True had a default
    # of '' (empty string), whereas with at least 1.11 that is None.
    # Force the former behaviour, since the documentation is not very clear
    # on what should happen.
    comment = CharField(max_length=255,
                        null=True,
                        blank=True,
                        editable=True,
                        default='')

    def __repr__(self):
        return ('IPRange(subnet_id=%r, start_ip=%r, end_ip=%r, type=%r, '
                'user_id=%r, comment=%r)') % (self.subnet_id, self.start_ip,
                                              self.end_ip, self.type,
                                              self.user_id, self.comment)

    def __contains__(self, item):
        return item in self.netaddr_iprange

    def _raise_validation_error(self, message, fields=None):
        if fields is None:
            # By default, highlight the start_ip and the end_ip.
            fields = ['start_ip', 'end_ip']
        validation_errors = {}
        for field in fields:
            validation_errors[field] = [message]
        raise ValidationError(validation_errors)

    def clean(self):
        super().clean()
        try:
            # XXX mpontillo 2015-12-22: I would rather the Django model field
            # just give me back an IPAddress, but changing it to do this was
            # had a much larger impact than I expected.
            start_ip = IPAddress(self.start_ip)
            end_ip = IPAddress(self.end_ip)
        except AddrFormatError:
            # This validation will be called even if the start_ip or end_ip
            # field is missing. So we need to check them again here, before
            # proceeding with the validation (and potentially crashing).
            self._raise_validation_error(
                "Start IP address and end IP address are both required.")
        if end_ip.version != start_ip.version:
            self._raise_validation_error(
                "Start IP address and end IP address must be in the same "
                "address family.")
        if end_ip < start_ip:
            self._raise_validation_error(
                "End IP address must not be less than Start IP address.",
                fields=['end_ip'])
        if self.subnet_id is not None:
            cidr = IPNetwork(self.subnet.cidr)
            if start_ip not in cidr and end_ip not in cidr:
                self._raise_validation_error(
                    "IP addresses must be within subnet: %s." % cidr)
            if start_ip not in cidr:
                self._raise_validation_error(
                    "Start IP address must be within subnet: %s." % cidr,
                    fields=['start_ip'])
            if end_ip not in cidr:
                self._raise_validation_error(
                    "End IP address must be within subnet: %s." % cidr,
                    fields=['end_ip'])
            if cidr.network == start_ip:
                self._raise_validation_error(
                    "Reserved network address cannot be included in IP range.",
                    fields=['start_ip'])
            if cidr.version == 4 and cidr.broadcast == end_ip:
                self._raise_validation_error(
                    "Broadcast address cannot be included in IP range.",
                    fields=['end_ip'])
        if (start_ip.version == 6 and self.type == IPRANGE_TYPE.DYNAMIC
                and netaddr.IPRange(start_ip, end_ip).size < 256):
            self._raise_validation_error(
                "IPv6 dynamic range must be at least 256 addresses in size.")
        self.clean_prevent_dupes_and_overlaps()

    @property
    def netaddr_iprange(self):
        return netaddr.IPRange(self.start_ip, self.end_ip)

    def get_MAASIPRange(self):
        purpose = self.type
        # Using '-' instead of '_' is just for consistency.
        # APIs in previous MAAS releases used '-' in range types.
        purpose = purpose.replace('_', '-')
        return make_iprange(self.start_ip, self.end_ip, purpose=purpose)

    @transactional
    def clean_prevent_dupes_and_overlaps(self):
        """Make sure the new or updated range isn't going to cause a conflict.
        If it will, raise ValidationError.
        """
        # Check against the valid types before going further, since whether
        # or not the range overlaps anything that could cause an error heavily
        # depends on its type.
        valid_types = {choice[0] for choice in IPRANGE_TYPE_CHOICES}

        # If model is incomplete, save() will fail, so don't bother checking.
        if (self.subnet_id is None or self.start_ip is None
                or self.end_ip is None or self.type is None
                or self.type not in valid_types):
            return

        # No dupe checking is required if the object hasn't been materially
        # modified.
        if not self._state.has_any_changed(['type', 'start_ip', 'end_ip']):
            return

        # Reserved ranges can overlap allocated IPs but not other ranges.
        # Dynamic ranges cannot overlap anything (no ranges or IPs).
        if self.type == IPRANGE_TYPE.RESERVED:
            unused = self.subnet.get_ipranges_available_for_reserved_range(
                exclude_ip_ranges=[self])
        else:
            unused = self.subnet.get_ipranges_available_for_dynamic_range(
                exclude_ip_ranges=[self])

        if len(unused) == 0:
            self._raise_validation_error(
                "There is no room for any %s ranges on this subnet." %
                (self.type))

        message = "Requested %s range conflicts with an existing " % self.type
        if self.type == IPRANGE_TYPE.RESERVED:
            message += "range."
        else:
            message += "IP address or range."

        # Find unused range for start_ip
        for range in unused:
            if IPAddress(self.start_ip) in range:
                if IPAddress(self.end_ip) in range:
                    # Success, start and end IP are in an unused range.
                    return
                else:
                    self._raise_validation_error(message)
        self._raise_validation_error(message)
コード例 #30
0
class TBar(Model):
    foo = ForeignKey(TFoo, on_delete=CASCADE)
    c = BooleanField()

    class Meta:
        ordering = ('pk',)