class Material(LocalizedModel): objects = MaterialQuerySet.as_manager() class Meta: ordering = ['-highlight', '-timestamp'] verbose_name = _("Material") verbose_name_plural = _("Materials") title = LocalizedCharField(blank=False, null=False, required=False, max_length=512, verbose_name=_("title")) slug = LocalizedUniqueSlugField(populate_from='title') timestamp = models.DateTimeField(auto_now_add=True, verbose_name=_("creation timestamp")) highlight = models.BooleanField(default=False, verbose_name=_("highlighted on search")) subjects = models.ManyToManyField(Subject, verbose_name=_("subject")) goal = LocalizedTextField(blank=True, null=True, required=False, verbose_name=_("goal")) brief = LocalizedTextField(blank=False, null=False, required=False, verbose_name=_("brief")) author = models.CharField(max_length=512, blank=True, verbose_name=_("author")) def __str__(self): return str(self.title) @property def brief_rendered(self): return markdownify(str(self.brief)) @property def goal_rendered(self): return markdownify(str(self.goal)) def get_related(self): for related in ['activity', 'reading', 'video', 'link']: if hasattr(self, related): return getattr(self, related) def get_model_name(self): return self.get_related()._meta.model_name def get_absolute_url(self): return self.get_related().get_absolute_url()
class Interest(UUIDModel, HistoricalModel): title = LocalizedCharField() description = LocalizedTextField(blank=True, null=True, required=False) category = models.ForeignKey(InterestCategory, related_name="interests", on_delete=models.PROTECT) archived = models.BooleanField(default=False)
class Permission(SlugModel): name = LocalizedCharField(_("permission name"), blank=False, null=False, required=False) description = LocalizedTextField(_("permission description"), null=True, blank=True, required=False)
class Tag(SlugModel): name = models.CharField(_("tag name"), blank=False, null=False, max_length=100) description = LocalizedTextField(_("tag description"), null=True, blank=True, required=False)
class Category(SlugModel): name = LocalizedCharField( _("category name"), blank=False, null=False, required=False ) description = LocalizedTextField( _("category description"), null=True, blank=True, required=False ) color = models.CharField( max_length=18, default="#FFFFFF", validators=[color_validator], )
class Role(SlugModel): name = LocalizedCharField(_("role name"), blank=False, null=False, required=False) description = LocalizedTextField(_("role description"), null=True, blank=True, required=False) permissions = models.ManyToManyField("Permission", related_name="roles") class Meta: ordering = ["slug"]
class Chunk(models.Model): class Meta: ordering = ['slug'] verbose_name = _("Chunk") verbose_name_plural = _("Chunks") slug = models.SlugField(unique=True, verbose_name=_("slug")) content = LocalizedTextField(blank=False, null=False, required=False, verbose_name=_("content")) def __str__(self): return str(self.slug)
class Document(UUIDModel): title = LocalizedCharField( _("document title"), blank=True, null=True, required=False ) description = LocalizedTextField( _("document description"), null=True, blank=True, required=False ) category = models.ForeignKey( Category, on_delete=models.SET_NULL, null=True, blank=True, related_name="documents", ) tags = models.ManyToManyField(Tag, blank=True, related_name="documents")
class Scope(MPTTModel, UUIDModel): name = LocalizedCharField(_("scope name"), blank=False, null=False, required=False) description = LocalizedTextField(_("scope description"), null=True, blank=True, required=False) parent = TreeForeignKey( "self", on_delete=models.SET_NULL, null=True, blank=True, related_name="children", )
class Activity(Material): objects = MaterialQuerySet.as_manager() class Meta: ordering = ['-timestamp'] verbose_name = _("Activity") verbose_name_plural = _("Activities") location = models.ForeignKey(Location, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("location")) duration = models.PositiveSmallIntegerField( null=True, blank=True, verbose_name=_("duration"), help_text=_("Duration in minutes."), ) num_people = IntegerRangeField(default=NumericRange(2, 30), verbose_name=_("number of people")) group_feature = models.ForeignKey(GroupFeature, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("group feature")) notes = LocalizedTextField(null=True, blank=True, verbose_name=_("notes")) attachment = models.FileField(upload_to='material/activities/', blank=True, verbose_name=_("attachment")) url = models.URLField( blank=True, verbose_name=_("URL"), help_text=_( "Link the material if its copyright does not allow sharing it.")) def get_absolute_url(self): return reverse('material:detail-activity', kwargs={'slug': self.slug}) def clean(self): if self.num_people.lower < 1: raise ValidationError(_("The lower bound must be at least 1."), code='invalid')
class Membership(UUIDModel, HistoricalModel): identity = models.ForeignKey( "Identity", on_delete=models.CASCADE, related_name="members", ) organisation = models.ForeignKey( "Identity", on_delete=models.CASCADE, related_name="memberships", ) role = models.ForeignKey( MembershipRole, related_name="memberships", on_delete=models.SET_NULL, null=True, blank=True, ) authorized = models.BooleanField(default=False) time_slot = DateRangeField(null=True, blank=True) next_election = models.DateField(null=True, blank=True) comment = LocalizedTextField(blank=True, null=True, required=False) inactive = models.BooleanField(default=False)
class Question(SlugModel): TYPE_MULTIPLE_CHOICE = "multiple_choice" TYPE_INTEGER = "integer" TYPE_FLOAT = "float" TYPE_DATE = "date" TYPE_CHOICE = "choice" TYPE_TEXTAREA = "textarea" TYPE_TEXT = "text" TYPE_TABLE = "table" TYPE_FORM = "form" TYPE_FILE = "file" TYPE_DYNAMIC_CHOICE = "dynamic_choice" TYPE_DYNAMIC_MULTIPLE_CHOICE = "dynamic_multiple_choice" TYPE_STATIC = "static" TYPE_CHOICES = ( TYPE_MULTIPLE_CHOICE, TYPE_INTEGER, TYPE_FLOAT, TYPE_DATE, TYPE_CHOICE, TYPE_TEXTAREA, TYPE_TEXT, TYPE_TABLE, TYPE_FORM, TYPE_FILE, TYPE_DYNAMIC_CHOICE, TYPE_DYNAMIC_MULTIPLE_CHOICE, TYPE_STATIC, ) TYPE_CHOICES_TUPLE = ((type_choice, type_choice) for type_choice in TYPE_CHOICES) label = LocalizedField(blank=False, null=False, required=False) type = models.CharField(choices=TYPE_CHOICES_TUPLE, max_length=23) is_required = models.TextField(default="false") is_hidden = models.TextField(default="false") is_archived = models.BooleanField(default=False) placeholder = LocalizedField(blank=True, null=True, required=False) info_text = LocalizedField(blank=True, null=True, required=False) static_content = LocalizedTextField(blank=True, null=True, required=False) configuration = JSONField(default=dict) meta = JSONField(default=dict) data_source = models.CharField(max_length=255, blank=True, null=True) options = models.ManyToManyField( "Option", through="QuestionOption", related_name="questions" ) row_form = models.ForeignKey( Form, blank=True, null=True, related_name="+", help_text="Form that represents rows of a TableQuestion", ) sub_form = models.ForeignKey( Form, blank=True, null=True, related_name="+", help_text="Form referenced in a FormQuestion", ) source = models.ForeignKey( "self", blank=True, null=True, related_name="+", help_text="Reference this question has been copied from", ) format_validators = ArrayField( models.CharField(max_length=255), blank=True, default=list ) @property def max_length(self): return self.configuration.get("max_length") @max_length.setter def max_length(self, value): self.configuration["max_length"] = value @property def max_value(self): return self.configuration.get("max_value") @max_value.setter def max_value(self, value): self.configuration["max_value"] = value @property def min_value(self): return self.configuration.get("min_value") @min_value.setter def min_value(self, value): self.configuration["min_value"] = value
class Question(core_models.SlugModel): TYPE_MULTIPLE_CHOICE = "multiple_choice" TYPE_INTEGER = "integer" TYPE_FLOAT = "float" TYPE_DATE = "date" TYPE_CHOICE = "choice" TYPE_TEXTAREA = "textarea" TYPE_TEXT = "text" TYPE_TABLE = "table" TYPE_FORM = "form" TYPE_FILE = "file" TYPE_DYNAMIC_CHOICE = "dynamic_choice" TYPE_DYNAMIC_MULTIPLE_CHOICE = "dynamic_multiple_choice" TYPE_STATIC = "static" TYPE_CALCULATED_FLOAT = "calculated_float" TYPE_CHOICES = ( TYPE_MULTIPLE_CHOICE, TYPE_INTEGER, TYPE_FLOAT, TYPE_DATE, TYPE_CHOICE, TYPE_TEXTAREA, TYPE_TEXT, TYPE_TABLE, TYPE_FORM, TYPE_FILE, TYPE_DYNAMIC_CHOICE, TYPE_DYNAMIC_MULTIPLE_CHOICE, TYPE_STATIC, TYPE_CALCULATED_FLOAT, ) TYPE_CHOICES_TUPLE = ((type_choice, type_choice) for type_choice in TYPE_CHOICES) label = LocalizedField(blank=False, null=False, required=False) type = models.CharField(choices=TYPE_CHOICES_TUPLE, max_length=23) is_required = models.TextField(default="false") is_hidden = models.TextField(default="false") is_archived = models.BooleanField(default=False) placeholder = LocalizedField(blank=True, null=True, required=False) info_text = LocalizedField(blank=True, null=True, required=False) static_content = LocalizedTextField(blank=True, null=True, required=False) configuration = JSONField(default=dict) meta = JSONField(default=dict) data_source = models.CharField(max_length=255, blank=True, null=True) options = models.ManyToManyField("Option", through="QuestionOption", related_name="questions") row_form = models.ForeignKey( Form, blank=True, null=True, related_name="+", help_text="Form that represents rows of a TableQuestion", on_delete=models.PROTECT, ) sub_form = models.ForeignKey( Form, blank=True, null=True, related_name="+", help_text="Form referenced in a FormQuestion", on_delete=models.PROTECT, ) source = models.ForeignKey( "self", blank=True, null=True, related_name="+", help_text="Reference this question has been copied from", on_delete=models.SET_NULL, ) format_validators = ArrayField(models.CharField(max_length=255), blank=True, default=list) default_answer = models.OneToOneField("Answer", on_delete=models.SET_NULL, related_name="+", null=True, blank=True) calc_expression = models.TextField(null=True, blank=True) calc_dependents = ArrayField(models.CharField(max_length=255, blank=True), default=list) @property def min_length(self): return self.configuration.get("min_length") @min_length.setter def min_length(self, value): self.configuration["min_length"] = value @property def max_length(self): return self.configuration.get("max_length") @max_length.setter def max_length(self, value): self.configuration["max_length"] = value @property def max_value(self): return self.configuration.get("max_value") @max_value.setter def max_value(self, value): self.configuration["max_value"] = value @property def min_value(self): return self.configuration.get("min_value") @min_value.setter def min_value(self, value): self.configuration["min_value"] = value def empty_value(self): """Return empty value for this question type.""" empties = { Question.TYPE_MULTIPLE_CHOICE: [], Question.TYPE_TABLE: [], Question.TYPE_DYNAMIC_MULTIPLE_CHOICE: [], } return empties.get(self.type, None) def __repr__(self): base = super().__repr__() return base[:-1] + f", type={self.type})" class Meta: indexes = [GinIndex(fields=["meta"])]
class InterestCategory(UUIDModel, HistoricalModel): title = LocalizedCharField() description = LocalizedTextField(blank=True, null=True, required=False) archived = models.BooleanField(default=False)
class Snippet(UUIDModel, HistoricalModel): title = LocalizedCharField(blank=False, null=False, required=False) body = LocalizedTextField(blank=False, null=False, required=False) archived = models.BooleanField(default=False)