class ProductVariationResult(models.Model): product = models.ForeignKey("Product", related_name='variation_result_supers', on_delete=models.CASCADE) combination_hash = models.CharField(max_length=40, unique=True, db_index=True) result = models.ForeignKey("Product", related_name='variation_result_subs', on_delete=models.CASCADE) status = EnumIntegerField(ProductVariationLinkStatus, db_index=True, default=ProductVariationLinkStatus.VISIBLE) @classmethod def resolve(cls, parent_product, combination): pvr = cls.objects.filter( product=parent_product, combination_hash=hash_combination(combination), status=ProductVariationLinkStatus.VISIBLE).first() if pvr: return pvr.result class Meta: verbose_name = _('variation result') verbose_name_plural = _('variation results')
class Task(models.Model): """ Model: 任务/日程 Fields: 创建时间(created), 创建者(owner), 任务标题(title), 任务详情(detail), 任务日期时间(date), 是否完成(isDone) """ user = models.ForeignKey(CustomUser, verbose_name='用户', blank=True, null=True, on_delete=models.CASCADE) subplan = models.ForeignKey(SubPlan, verbose_name='子计划', blank=True, null=True, on_delete=models.CASCADE) title = models.CharField('标题', max_length=100, blank=True, default='新任务') content = models.TextField('内容', blank=True, default='') label = EnumIntegerField(Label, verbose_name='重要紧急标签', default=Label.IMPORTANT_URGENT) created_datetime = models.DateTimeField('创建时间', auto_now_add=True) update_datetime = models.DateTimeField('修改时间', auto_now=True) is_archived = models.BooleanField('是否归档', default=False) rawtext = models.BooleanField('是否纯文本', default=True) is_finished = models.BooleanField('是否完成', default=False) finished_datetime = models.DateTimeField('完成时间', blank=True, null=True, default=None) is_all_day = models.BooleanField('是否全天', default=True) begin_datetime = models.DateTimeField('开始时间', blank=True, null=True, default=None) end_datetime = models.DateTimeField('结束时间', blank=True, null=True, default=None) class Meta: db_table = "Schedules" ordering = ('update_datetime', ) verbose_name = '日程' verbose_name_plural = verbose_name
class StockAdjustment(models.Model): product = models.ForeignKey("wshop.Product", related_name="+", on_delete=models.CASCADE, verbose_name=_("product")) supplier = models.ForeignKey("wshop.Supplier", on_delete=models.CASCADE, verbose_name=_("supplier")) created_on = models.DateTimeField(auto_now_add=True, editable=False, db_index=True, verbose_name=_("created on")) created_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.PROTECT, verbose_name=_("created by")) delta = QuantityField(default=0, verbose_name=_("delta")) purchase_price_value = MoneyValueField(default=0) purchase_price = PriceProperty("purchase_price_value", "currency", "includes_tax") type = EnumIntegerField(StockAdjustmentType, db_index=True, default=StockAdjustmentType.INVENTORY, verbose_name=_("type")) @cached_property def currency(self): return _get_currency() @cached_property def includes_tax(self): return _get_prices_include_tax()
class CategoryProductsBasketCondition(BasketCondition): identifier = "basket_category_condition" name = _("Category products in basket") operator = EnumIntegerField(ComparisonOperator, default=ComparisonOperator.GTE, verbose_name=_("operator")) quantity = models.PositiveIntegerField(default=1, verbose_name=_("quantity")) category = models.ForeignKey(Category, verbose_name=_("category"), blank=True) def matches(self, basket, lines): product_id_to_qty = get_product_ids_and_quantities(basket) category_product_ids = self.category.shop_products.filter( product_id__in=product_id_to_qty.keys()).values_list("product_id", flat=True) product_count = sum(product_id_to_qty[product_id] for product_id in category_product_ids) if self.operator == ComparisonOperator.EQUALS: return bool(product_count == self.quantity) else: return bool(product_count >= self.quantity) @property def description(self): return _( "Limit the campaign to match the products from selected category.")
class Downtime(models.Model): status = EnumIntegerField(Status, validators=[validate_not_normal]) tweet_start = models.ForeignKey("tweetdb.Tweet", on_delete=models.CASCADE, related_name="+") tweet_end = models.ForeignKey("tweetdb.Tweet", blank=True, null=True, on_delete=models.SET_NULL, related_name="+") # This is denormalized for performance start = models.DateTimeField() end = models.DateTimeField(blank=True, null=True) def get_latest_status(self): if self.end is not None: return Status.NORMAL else: return self.status def get_duration(self): target = self.end or timezone.now() return (target - self.start).total_seconds() def __str__(self): return f"{self.status.name} from {self.start} - {self.end}"
class Player(models.Model): id: str = models.CharField(max_length=50, primary_key=True, editable=False) realm: str = models.CharField(max_length=10) region: str = EnumField(Region, max_length=2) rank: Rank = EnumIntegerField(enum=Rank) username: str = models.CharField(max_length=30) bnet_id: str = models.CharField(max_length=30, null=True, blank=True) race: Race = EnumField(Race, max_length=7) mmr: int = models.IntegerField() wins: int = models.IntegerField() losses: int = models.IntegerField() clan: str = models.CharField(max_length=10, null=True, blank=True) profile_id: int = models.IntegerField() identity = models.ForeignKey(Identity, null=True, blank=True, on_delete=models.SET_NULL) game_link = models.CharField( max_length=25, null=True, blank=True) # prepend with battlenet:://starcraft/profile/ mmr_history = JSONField(default=dict) created_at: datetime = models.DateTimeField(auto_now_add=True) modified_at: datetime = models.DateTimeField(auto_now=True) players = PlayerManager() actives = ActivePlayerManager() def __str__(self): return self.id
class Patient(AuditableModel): mrn = models.CharField(max_length=128, verbose_name='medical record number', primary_key=True) first_name = models.CharField(max_length=256, blank=True) last_name = models.CharField(max_length=256, blank=True) dob = models.DateField('date of birth', blank=True, null=True) approx_age = models.PositiveIntegerField('approx. age', blank=True, null=True) gender = EnumIntegerField(Gender, verbose_name='gender', default=Gender.UNKNOWN, blank=True, null=True) def __str__(self): return f'{self.first_name} {self.last_name} (MRN: {self.mrn})' @property def age(self): """ Calculate age using DOB if possible, else use the `approx_age` field """ if self.dob: now = timezone.make_naive(timezone.localtime()) return relativedelta(now, self.dob).years if self.approx_age: return self.approx_age return '?' @property def name(self): return f'{self.first_name} {self.last_name}'
class QuestionGroup(OrderableModel, AuditableModel): """ A QuestionGroup is used to group Questions together and associate them with a parent object (Syndrome, Diagnosis, etc.) through a generic foreign key. """ title = models.CharField(max_length=256, null=True, blank=True) category = EnumIntegerField(QuestionCategory) questions = models.ManyToManyField( Question, related_name='groups', through='OrderedQuestion', through_fields=('question_group', 'question'), ) # Generic foreign key fields content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') class Meta(OrderableModel.Meta): constraints = [ models.UniqueConstraint( fields=['content_type', 'object_id', 'id'], condition=models.Q(category=QuestionCategory.ISSUE_RESULT), name='issues_can_only_have_one_question_group', ) ] def __str__(self): return self.title or '(no title)'
class Question(AuditableModel): title = models.CharField(max_length=256) speciality = models.ForeignKey(Speciality, on_delete=models.CASCADE) type = EnumIntegerField(QuestionType, default=QuestionType.BOOLEAN) category = EnumIntegerField(QuestionCategory) help_text = models.CharField(max_length=1000, blank=True, null=True) # The label is used to uniquely identify a Question (across all specialities) and is used # when getting data from user created questions for charts, dashboards etc. label = models.SlugField(max_length=100, unique=True, blank=True, null=True) def __str__(self): return self.title
class Shipment(models.Model): order = models.ForeignKey("Order", related_name='shipments', on_delete=models.PROTECT) supplier = models.ForeignKey("Supplier", related_name='shipments', on_delete=models.PROTECT) created_on = models.DateTimeField(auto_now_add=True) status = EnumIntegerField(ShipmentStatus, default=ShipmentStatus.NOT_SENT) tracking_code = models.CharField(max_length=64, blank=True, verbose_name=_('tracking code')) description = models.CharField(max_length=255, blank=True) volume = MeasurementField(unit="m3") weight = MeasurementField(unit="kg") # TODO: documents = models.ManyToManyField(FilerFile) class Meta: verbose_name = _('shipment') verbose_name_plural = _('shipments') def __repr__(self): # pragma: no cover return "<Shipment %s for order %s (tracking %r, created %s)>" % ( self.pk, self.order_id, self.tracking_code, self.created_on ) def cache_values(self): """ (Re)cache `.volume` and `.weight` for this Shipment from the ShipmentProducts within. """ total_volume = 0 total_weight = 0 for quantity, volume, weight in self.products.values_list("quantity", "unit_volume", "unit_weight"): total_volume += quantity * volume total_weight += quantity * weight self.volume = total_volume self.weight = total_weight @property def total_products(self): return (self.products.aggregate(quantity=models.Sum("quantity"))["quantity"] or 0)
class Plan(models.Model): """ Model: 任务/计划 Fields: 创建时间(created), 创建者(owner), 任务标题(title), 任务详情(detail), 任务日期时间(date), 是否完成(isDone) """ user = models.ForeignKey(CustomUser, verbose_name='用户', blank=True, null=True, on_delete=models.CASCADE) title = models.CharField('标题', max_length=100, blank=True, default='新任务') content = models.TextField('内容', blank=True, default='') label = EnumIntegerField(Label, verbose_name='重要紧急标签', default=Label.IMPORTANT_URGENT) cover = models.TextField('封面', blank=True, default='') created_datetime = models.DateTimeField('创建时间', auto_now_add=True) update_datetime = models.DateTimeField('修改时间', auto_now=True) is_archived = models.BooleanField('是否归档', default=False) is_star = models.BooleanField('是否星标', default=False) class Meta: db_table = "Plans" ordering = ('update_datetime', ) verbose_name = '计划' verbose_name_plural = verbose_name
class OrderStatus(TranslatableModel): identifier = InternalIdentifierField(db_index=True, blank=False, unique=True) ordering = models.IntegerField(db_index=True, default=0, verbose_name=_('ordering')) role = EnumIntegerField(OrderStatusRole, db_index=True, default=OrderStatusRole.NONE, verbose_name=_('role')) default = models.BooleanField(default=False, db_index=True, verbose_name=_('default')) objects = OrderStatusQuerySet.as_manager() translations = TranslatedFields( name=models.CharField(verbose_name=_("name"), max_length=64)) def __str__(self): return self.safe_translation_getter("name", default=self.identifier) def save(self, *args, **kwargs): super(OrderStatus, self).save(*args, **kwargs) if self.default and self.role != OrderStatusRole.NONE: # If this status is the default, make the others for this role non-default. OrderStatus.objects.filter(role=self.role).exclude( pk=self.pk).update(default=False)
class Carousel(ShuupModel): shops = models.ManyToManyField( "shuup.Shop", related_name="carousels", verbose_name=_('shops'), help_text=_( "Select which shops you would like the carousel to be visible in.") ) name = models.CharField( max_length=50, verbose_name=_("name"), help_text=_("The carousel name use for carousel configuration.")) animation = EnumIntegerField( CarouselMode, default=CarouselMode.SLIDE, verbose_name=_("animation"), help_text=_("Animation type for cycling slides.")) interval = models.IntegerField(default=5, verbose_name=_("interval"), help_text=_("Slide interval in seconds.")) pause_on_hover = models.BooleanField( default=True, verbose_name=_("pause on hover"), help_text=_( "When checked, the carousel cycling pauses on mouse over.")) is_arrows_visible = models.BooleanField( default=True, verbose_name=_("show navigation arrows"), help_text= _("When checked, navigational arrows are shown on the carousel allowing for customers to go back and forward." )) use_dot_navigation = models.BooleanField( default=True, verbose_name=_("show navigation dots"), help_text=_("When checked, navigational indicator dots are shown.")) image_width = models.IntegerField( default=1200, verbose_name=_("image width"), help_text=_("Slide images will be cropped to this width.")) image_height = models.IntegerField( default=600, verbose_name=_("image height"), help_text=_("Slide images will be cropped to this height.")) arrows_color = HexColorField( verbose_name=_("Arrows color"), blank=True, null=True, help_text=_("Set the custom color for the arrows."), ) def __str__(self): return self.name class Meta: verbose_name = _("Carousel") verbose_name_plural = _("Carousels") @property def animation_class_name(self): return "fade" if self.animation == CarouselMode.FADE else "slide"
class WalletTransaction(UUIDModel, TimeStampedModel): wallet = models.ForeignKey(Wallet, related_name='transactions') amount = MoneyField(max_digits=10, decimal_places=2, default_currency=settings.DEFAULT_CURRENCY, help_text=_('Positive amount to deposit money. ' 'Negative amount to withdraw money.')) trx_type = EnumIntegerField(enums.TrxType, verbose_name=_('type'), default=enums.TrxType.FINALIZED) reference = models.CharField(max_length=128, blank=True, null=True) internal_reference = models.ForeignKey('self', blank=True, null=True) objects = WalletTrxsQuerySet.as_manager() class Meta: verbose_name = _('transaction') verbose_name_plural = _('transactions') @property def signed_amount(self): """Returns the amount in a signed form based on the trx type""" return self.amount @property def countable(self): """Returns a boolean depending on if the transactions counts or not. For example, rejected transactions do not contribute to the wallet balance. """ return self.trx_type != enums.TrxType.PENDING
class UserRelationsInRoom(models.Model): user1 = models.ForeignKey(Account, on_delete=models.CASCADE, related_name="user1") user2 = models.ForeignKey(Account, on_delete=models.CASCADE, related_name="user2") user1_to_user2_state = EnumIntegerField(RelationStateType, default=RelationStateType.NONE) class Meta: unique_together = (("user1", "user2"),)
class CheckResult(models.Model): ''' The result of a check. These are raw time series, evaluated to produce State objects. ''' objects = CheckResultManager() state = models.ForeignKey('State', related_name='check_results') source_type = models.ForeignKey(ContentType) source_id = models.PositiveIntegerField() source = GenericForeignKey('source_type', 'source_id') name = models.CharField(max_length=255) host = models.CharField(max_length=255, null=True) status = EnumIntegerField(Status) output = models.CharField(max_length=4095, null=True) metrics = JSONField(null=True) timestamp = models.DateTimeField(auto_now_add=True, db_index=True) @classmethod def from_dict(cls, source, data): return cls(source=source, **data) class Meta: index_together = ['source_type', 'source_id']
class ProductsInBasketCondition(BasketCondition): identifier = "basket_products_condition" name = _("Products in basket") model = Product operator = EnumIntegerField( ComparisonOperator, default=ComparisonOperator.GTE, verbose_name=_("operator")) quantity = models.PositiveIntegerField(default=1, verbose_name=_("quantity")) products = models.ManyToManyField(Product, verbose_name=_("products"), blank=True) def matches(self, basket, lines): campaign = self.campaign.first() supplier = campaign.supplier if hasattr(campaign, "supplier") and campaign.supplier else None product_id_to_qty = get_product_ids_and_quantities(basket, supplier) product_ids = self.products.filter(id__in=product_id_to_qty.keys()).values_list("id", flat=True) for product_id in product_ids: if self.operator == ComparisonOperator.GTE: return product_id_to_qty[product_id] >= self.quantity elif self.operator == ComparisonOperator.EQUALS: return product_id_to_qty[product_id] == self.quantity return False @property def description(self): return _("Limit the campaign to have the selected products in basket.") @property def values(self): return self.products @values.setter def values(self, value): self.products = value
class Cluster(models.Model): name = models.CharField(max_length=256) description = models.CharField(max_length=128, blank=True) kind = EnumIntegerField(ClusterType) members = models.ManyToManyField('Host') virtual_machines = GenericRelation('Host', content_type_field='parent_type', object_id_field='parent_id') def __str__(self): return 'cluster: {}'.format(self.name) def delete(self, *args, **kwargs): """ Django's default behavior will cascade deletes here, causing the deletion of a cluster to delete all of that clusters's members and hosts. So throw an error if the cluster has any remaining VMs. Otherwise, delete the members from the object before calling delete on it. """ if self.virtual_machines.all(): children = [vm.hostname for vm in self.virtual_machines.all()] raise RuntimeError( 'cannot delete cluster until its hosts have been reassigned: {}' .format(children)) for member in self.members.all(): self.members.remove(member) self.save() super(Cluster, self).delete(*args, **kwargs)
class CategoryProductsBasketCondition(BasketCondition): model = Category identifier = "basket_category_condition" name = _("Category products in basket") operator = EnumIntegerField( ComparisonOperator, default=ComparisonOperator.GTE, verbose_name=_("operator")) quantity = models.PositiveIntegerField(default=1, verbose_name=_("quantity")) categories = models.ManyToManyField(Category, related_name="+", verbose_name=_("categories")) excluded_categories = models.ManyToManyField( Category, blank=True, related_name="+", verbose_name=_("excluded categories"), help_text=_( "If the customer has even a single product in the basket from these categories " "this rule won't match thus the campaign cannot be applied to the basket." )) def matches(self, basket, lines): product_id_to_qty = get_product_ids_and_quantities(basket) if ShopProduct.objects.filter( product_id__in=product_id_to_qty.keys(), categories__in=self.excluded_categories.all()).exists(): return False product_ids = ShopProduct.objects.filter( categories__in=self.categories.all(), product_id__in=product_id_to_qty.keys() ).values_list("product_id", flat=True) product_count = sum(product_id_to_qty[product_id] for product_id in product_ids) if self.operator == ComparisonOperator.EQUALS: return bool(product_count == self.quantity) else: return bool(product_count >= self.quantity) @property def description(self): return _("Limit the campaign to match the products from selected categories.")
class GDACSEvent(models.Model): """ A GDACS type event, from alerts """ eventid = models.CharField(max_length=12) title = models.TextField() description = models.TextField() image = models.URLField(null=True) report = models.URLField(null=True) publication_date = models.DateTimeField() year = models.IntegerField() lat = models.FloatField() lon = models.FloatField() event_type = models.CharField(max_length=16) alert_level = EnumIntegerField(AlertLevel, default=0) alert_score = models.CharField(max_length=16, null=True) severity = models.TextField() severity_unit = models.CharField(max_length=16) severity_value = models.CharField(max_length=16) population_unit = models.CharField(max_length=16) population_value = models.CharField(max_length=16) vulnerability = models.IntegerField() countries = models.ManyToManyField(Country) country_text = models.TextField() def __str__(self): return self.title
class Purchase(UUIDModel, TimeStampedModel): # account is None for cash payments account = models.ForeignKey(Account, related_name='purchases', null=True, blank=True) status = EnumIntegerField(enums.PurchaseStatus, default=enums.PurchaseStatus.FINALIZED) amount = MoneyField(max_digits=10, decimal_places=2, default_currency=settings.DEFAULT_CURRENCY) class Meta: ordering = ['-date_created'] @property def deletable(self): max_delta = settings.PURCHASE_CANCEL_MAX_DELTA return timezone.now() - self.date_created <= max_delta def payment_method(self): return _('Cash') if self.account is None else _('FooCard') def __str__(self): return str(self.id)
class NiceDocument(models.Model): created_at = models.DateTimeField(verbose_name=_('created at'), auto_now_add=True) name = models.CharField(verbose_name=_('name'), max_length=100) document = models.FileField(verbose_name=_('document'), null=True, blank=True, upload_to=nice_document_path) document_url = models.URLField(verbose_name=_('document url'), blank=True) country = models.ForeignKey(Country, verbose_name=_('country'), related_name='perdoc_country', null=True, blank=True, on_delete=models.SET_NULL) visibility = EnumIntegerField(Visibilities, verbose_name=_('visibility'), default=Visibilities.VISIBLE) class Meta: ordering = ('visibility', 'country') verbose_name = _('PER Document') verbose_name_plural = _('PER Documents') def __str__(self): return '%s - %s' % (self.country, self.name)
class Job(models.Model): user = models.ForeignKey( UserModel, related_name='jobs', on_delete=models.CASCADE, ) slug = models.CharField( unique=True, max_length=8, default=functools.partial(get_random_string, 8, 'acefhkjprutwvyx'), ) state = EnumIntegerField(StateEnum, default=StateEnum.NEW) title = models.CharField(max_length=255) job_type = EnumIntegerField(JobTypeEnum) location = models.CharField(max_length=255) apply_url = models.URLField() description = models.TextField() tags = models.ManyToManyField( 'jobs_tags.Tag', blank=True, through='jobs_tags.JobTag', through_fields=('job', 'tag'), ) created = models.DateTimeField(default=timezone.now) updated = models.DateTimeField(default=timezone.now) objects = JobManager() class Meta: ordering = ('-created',) get_latest_by = 'created' def get_absolute_url(self): return reverse('jobs:view', args=(slugify(self.title), self.slug)) def __str__(self): return "pk={0.pk} slug={0.slug} title={0.title!r}".format(self) def set_state(self, new_state, *args, **kwargs): dispatch(self, self.state, new_state, *args, **kwargs) self.state = new_state
class ProductMedia(TranslatableModel): identifier = InternalIdentifierField() product = models.ForeignKey("Product", related_name="media") shops = models.ManyToManyField("Shop", related_name="product_media") kind = EnumIntegerField( ProductMediaKind, db_index=True, default=ProductMediaKind.GENERIC_FILE, verbose_name=_('kind') ) file = FilerFileField(blank=True, null=True, verbose_name=_('file')) external_url = models.URLField(blank=True, null=True, verbose_name=u'URL') ordering = models.IntegerField(default=0) # Status enabled = models.BooleanField(db_index=True, default=True, verbose_name=_("enabled")) public = models.BooleanField(default=True, blank=True, verbose_name=_('public (shown on product page)')) purchased = models.BooleanField( default=False, blank=True, verbose_name=_('purchased (shown for finished purchases)') ) translations = TranslatedFields( title=models.CharField(blank=True, max_length=128, verbose_name=_('title')), description=models.TextField(blank=True, verbose_name=_('description')), ) class Meta: verbose_name = _('product attachment') verbose_name_plural = _('product attachments') ordering = ["ordering", ] def __str__(self): # pragma: no cover return self.effective_title @property def effective_title(self): title = self.safe_translation_getter("title") if title: return title if self.file_id: return self.file.label if self.external_url: return self.external_url return _('attachment') @property def url(self): if not self.public: raise ValueError("`get_effective_url()` may not be used on non-public media") if self.file_id: return self.file.url else: return self.external_url @property def easy_thumbnails_thumbnailer(self): if self.file_id: return get_thumbnailer(self.file)
class CountrySnippet(models.Model): country = models.ForeignKey(Country, related_name='snippets', on_delete=models.CASCADE) snippet = models.TextField(null=True, blank=True) image = models.ImageField(null=True, blank=True, upload_to='countries/%Y/%m/%d/', storage=AzureStorage()) visibility = EnumIntegerField(VisibilityChoices, default=3) position = EnumIntegerField(PositionType, default=3) class Meta: ordering = ( 'position', 'id', )
class Message(models.Model): id = models.CharField(primary_key=True, default=random_id, max_length=36) async_job = models.ForeignKey(AsyncJob, on_delete=models.CASCADE) type = EnumIntegerField(MessageType) body = JSONField() created_at = models.DateTimeField(auto_now_add=True)
class Event(models.Model): name = BleachTextField(max_length=512) description = BleachTextField(max_length=2048, null=True) start = models.DateTimeField() end = models.DateTimeField() location = BleachTextField(max_length=2048) frequency = EnumIntegerField(Frequency, default=Frequency.none) contact = models.ForeignKey(User, related_name="events", null=True)
class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile") ui_darkness = EnumIntegerField(UISettinsEnum, default=UISettinsEnum.LIGHT, blank=False, null=False)
class Task(models.Model): shop = models.ForeignKey(on_delete=models.CASCADE, to="shuup.Shop", verbose_name=_("shop"), related_name="tasks") name = models.CharField(verbose_name=_("name"), max_length=60) type = models.ForeignKey(on_delete=models.CASCADE, to=TaskType, verbose_name=_("task type"), related_name="tasks") status = EnumIntegerField(TaskStatus, default=TaskStatus.NEW, verbose_name=_("status")) priority = models.PositiveIntegerField(default=0, verbose_name=_("priority"), db_index=True) creator = models.ForeignKey( on_delete=models.CASCADE, to="shuup.Contact", blank=True, null=True, related_name="creted_tasks", verbose_name=_("creator") ) assigned_to = models.ForeignKey( on_delete=models.CASCADE, to="shuup.Contact", blank=True, null=True, related_name="assigned_tasks", verbose_name=_("assigned to") ) completed_by = models.ForeignKey( on_delete=models.CASCADE, to="shuup.Contact", blank=True, null=True, related_name="completed_tasks", verbose_name=_("completed by") ) completed_on = models.DateTimeField(verbose_name=_("completed on"), null=True, blank=True) created_on = models.DateTimeField(auto_now_add=True, editable=False, db_index=True, verbose_name=_("created on")) modified_on = models.DateTimeField(auto_now=True, editable=False, db_index=True, verbose_name=_("modified on")) objects = TaskQuerySet.as_manager() def __str__(self): return self.name def assign(self, user): self.assigned_to = user self.status = TaskStatus.IN_PROGRESS self.save() def delete(self): self.status = TaskStatus.DELETED self.save(update_fields=["status"]) self.add_log_entry("Success! Deleted (soft).", kind=LogEntryKind.DELETION) def comment(self, contact, comment, visibility=TaskCommentVisibility.PUBLIC): comment = TaskComment(task=self, author=contact, body=comment, visibility=visibility) comment.full_clean() comment.save() return comment def set_in_progress(self): self.status = TaskStatus.IN_PROGRESS self.add_log_entry("Info! In progress.", kind=LogEntryKind.EDIT) self.save() def set_completed(self, contact): self.completed_by = contact self.completed_on = now() self.status = TaskStatus.COMPLETED self.add_log_entry("Success! Completed.", kind=LogEntryKind.EDIT) self.save() def get_completion_time(self): if self.completed_on: return (self.completed_on - self.created_on)
class VendorReview(models.Model): shop = models.ForeignKey("shuup.Shop", verbose_name=_("shop"), related_name="supplier_reviews", on_delete=models.CASCADE) supplier = models.ForeignKey("shuup.Supplier", verbose_name=_("supplier"), related_name="supplier_reviews", on_delete=models.CASCADE) reviewer = models.ForeignKey("shuup.Contact", verbose_name=_("reviewer"), related_name="supplier_reviews", on_delete=models.CASCADE) rating = models.PositiveIntegerField( verbose_name=_("rating"), validators=[MaxValueValidator(5), MinValueValidator(1)]) comment = models.TextField(blank=True, null=True, verbose_name=_("comment")) would_recommend = models.BooleanField( default=False, verbose_name=_("Would recommend to a friend?"), help_text=_( "Indicates whether you would recommend this product to a friend.")) status = EnumIntegerField(ReviewStatus, db_index=True, default=ReviewStatus.PENDING) created_on = models.DateTimeField(auto_now_add=True, db_index=True) modified_on = models.DateTimeField(auto_now=True) option = models.ForeignKey(VendorReviewOption, blank=True, null=True, on_delete=models.SET_NULL, related_name="vendor_review_options") objects = VendorReviewQuerySet.as_manager() def __str__(self): return _("{option}Review for {supplier} by {reviewer_name}").format( option=(self.option.name + " " if self.option else ""), supplier=self.supplier, reviewer_name=self.reviewer.name) def save(self, *args, **kwargs): super(VendorReview, self).save(*args, **kwargs) recalculate_aggregation(self.supplier, self.option) from shuup_vendor_reviews.utils import bump_star_rating_cache bump_star_rating_cache(self.supplier.pk, (self.option.pk if self.option else "")) def approve(self): self.status = ReviewStatus.APPROVED self.save() def reject(self): self.status = ReviewStatus.REJECTED self.save()
def test_django_admin_lookup_value_for_integer_enum_field(): field = EnumIntegerField(Taste) assert field.get_prep_value(str(Taste.BITTER)) == 3, "get_prep_value should be able to convert from strings"