class Picture(models.Model): gallery = models.ForeignKey('gallery.Gallery', related_name="pictures") image = ProcessedImageField(upload_to=_image_upload_to, processors=[ResizeToFill(640, 480)], format='JPEG', options={'quality': 90}) preview = ProcessedImageField(upload_to=_preview_upload_to, processors=[ResizeToFill(320, 240)], format='JPEG', options={'quality': 90}) thumbnail = ProcessedImageField(upload_to=_thumbnail_upload_to, processors=[ResizeToFill(100, 75)], format='JPEG', options={'quality': 90}) # metadata created_by = models.ForeignKey(Account, related_name="pictures_created") created_on = models.DateTimeField(auto_now_add=True) updated_by = models.ForeignKey(Account, related_name="pictures_updated") updated_on = models.DateTimeField(auto_now=True) def __unicode__(self): return u"%s" % self.id
class Sprite(DateTimeModel): def __unicode__(self): return self.name name = models.CharField(max_length=50) image = ProcessedImageField( [ResizeToFill(96, 96)], upload_to=unique_filename, format='PNG', options={'quality': 80}) def get_pokemon(self): nm = self.name.split('_')[0] pokes = Pokemon.objects.filter( name=nm.lower() ) if pokes.exists(): return dict( name=pokes[0].name, resource_uri='/api/v1/pokemon/' + str(pokes[0].pkdx_id) + '/') return [] pokemon = property(fget=get_pokemon)
class Team(Model): link = SlugField(unique=True) # team url name links = ManyToManyField('link.Link', null=True, blank=True) # team social media links name = CharField(max_length=1024, unique=True) country = CountryField() members = ManyToManyField(Account, null=True, blank=True, related_name="teams") logo = ProcessedImageField(upload_to=_upload_to, processors=[ResizeToFill(525, 100)], format='PNG', options={'quality': 90}) active = BooleanField(default=False) application = TextField() # meta created_by = ForeignKey(Account, related_name='team_created') created_on = DateTimeField(auto_now_add=True) updated_by = ForeignKey(Account, related_name='team_updated') updated_on = DateTimeField(auto_now=True) def __unicode__(self): return u"%s (%s)" % (self.name, self.country.name) class Meta: ordering = ['name']
class DidRecipe(models.Model): """docstring for DidRecipe""" recipe = models.ForeignKey(Recipe) user = models.ForeignKey(User) image = ProcessedImageField(upload_to=did_get_file_path, null=True, blank=True, verbose_name=u'Cover image', processors=[ Adjust(contrast=1.2, sharpness=1.1), ResizeToFit(width=540, upscale=True) ], format='JPEG', options={'quality': 90}) comment = models.TextField() date = models.DateTimeField(auto_now_add=True) def __unicode__(self): return u"%s's %s" % (self.user.username, self.recipe.name) @models.permalink def get_absolute_url(self): return ('did_recipe_detail', (), {'pk': self.id}) class Meta: ordering = ['recipe', '-date']
class Recipe(models.Model): """docstring for Recipe""" name = models.CharField(db_index=True, max_length=200) author = models.ForeignKey(User) date = models.DateField(auto_now_add=True) category = models.ManyToManyField(RecipeCategory, verbose_name=u'list of categories') brief = models.TextField() ingredients = models.ManyToManyField(Food, through='Amount', verbose_name=u"list of ingredients") cover_image = ProcessedImageField(upload_to=get_file_path, null=True, blank=True, verbose_name=u'Cover image', processors=[ Adjust(contrast=1.2, sharpness=1.1), ResizeToFit(width=540, upscale=True) ], format='JPEG', options={'quality': 90}) tips = models.TextField(blank=True) did_num = models.PositiveIntegerField(default=0) like_num = models.PositiveIntegerField(default=0) view_num = models.PositiveIntegerField(default=0) trend_num = models.FloatField(default=0.0) today_view_num = models.PositiveIntegerField(default=0) prep_time = models.TimeField() cook_time = models.TimeField() cumulative_score = models.IntegerField(default=0) rating_num = models.IntegerField(default=0) def rating(self): result = float(self.cumulative_score) if self.rating_num == 0: return "0.0" result = result / self.rating_num return "%g" % round(result, 1) def rating_int(self): result = float(self.cumulative_score) if self.rating_num == 0: return 0 result = result / self.rating_num return int(round(result)) def __unicode__(self): return self.name @models.permalink def get_absolute_url(self): return ('recipe_detail', (), {'pk': self.id}) class Meta: ordering = ['-view_num', '-like_num']
class News(models.Model): """ Модель новости""" name = models.CharField(verbose_name='название', max_length=256, unique=True) slug = models.SlugField(verbose_name='слаг', max_length=128, unique=True) description = models.TextField(verbose_name='описание', blank=True) # image = models.ImageField(upload_to='news_avatars', blank=True) image = ProcessedImageField(upload_to='news_avatars', processors=[ResizeToFill(840, 470)], format="JPEG", options={'quality': 90}, blank=True) creation_date = models.DateTimeField(verbose_name='создан', auto_now_add=True, auto_now=False) updated = models.DateTimeField(verbose_name='обновлен', auto_now=True) def get_absolute_url(self): return reverse('news:news_detail', args=[str(self.id)]) class Meta: ordering = ('-updated', ) verbose_name = 'Новость' verbose_name_plural = 'Новости' def get_products(self): return self.solutions.select_related() def __unicode__(self): return self.name
class Account(Model): # main data user = ForeignKey('auth.User', unique=True, related_name="accounts") description = TextField(blank=True) source = CharField(max_length=64, choices=SOURCE_CHOICES, default='OTHER') mobile = CharField(max_length=1024, blank=True) links = ManyToManyField('link.Link', null=True, blank=True) passport = ProcessedImageField(upload_to=_upload_to, null=True, blank=True, processors=[ResizeToFit(1024, 768)], format='JPEG', options={'quality': 90}) # meta # created_by = self # updated_by = self created_on = DateTimeField(auto_now_add=True) updated_on = DateTimeField(auto_now=True) def get_name(): return self.user.username def __unicode__(self): return self.user.username class Meta: ordering = ['user__username']
class TechnicalSolutions(models.Model): """ Готовые технические решения (группа материалов собранных в готовый к применению продукт "под ключ" и продвигаемый на рынок) """ name = models.CharField(verbose_name='название материала', max_length=128, unique=True) slug = models.SlugField(verbose_name='слаг', max_length=128, unique=True) measure = models.ForeignKey(MeasureTypes, verbose_name='Единица измерения', on_delete=models.CASCADE, default=1) image = ProcessedImageField(upload_to='products_images', processors=[ResizeToFill(370, 220)], format='JPEG', options={'quality': 60}) alt_desc = models.CharField(verbose_name='alt фотографии', max_length=500, blank=True) short_desc = models.TextField(verbose_name='краткое описание материала', blank=True, null=True) description = models.TextField(verbose_name='описание материала', blank=True) price = models.DecimalField(verbose_name='цена', max_digits=8, decimal_places=2, default=0) created = models.DateTimeField(auto_now_add=True, auto_now=False) updated = models.DateTimeField(verbose_name='обновлен', auto_now_add=False, auto_now=True) is_active = models.BooleanField(verbose_name='активен', default=True) def get_absolute_url(self): return reverse('products:product', args=[str(self.slug)]) def __str__(self): return f"{self.name}" def get_projects(self): return self.projects.select_related().distinct('project_id') def get_works(self): return self.works.select_related().order_by('pk') def get_all_docs(self): pass class Meta: verbose_name = 'Техническое решение' verbose_name_plural = 'Технические решения'
class BaseThumbnailImageModel(models.Model): class Meta: abstract = True UPLOAD_PATH: str thumbnail = ProcessedImageField( upload_to=get_static_asset_upload_path, processors=[ResizeToFill(380, 575)], format="JPEG", options={"quality": 90}, blank=True, null=False, default="media/front/default_380x575.jpeg", help_text=(f"Image must be above 380x575px"), )
class Pictures(models.Model): brab = models.ForeignKey(Brab, blank=True, null=True, help_text="Brab") title = models.TextField(null=True, help_text="title") # picture = models.ImageField(upload_to= 'pictures/%Y/%m/%d', height_field="pic_height", width_field="pic_width", null=True, blank=True, max_length=250, help_text="Picture URL") # picture = ProcessedImageField([Transpose(Transpose.AUTO), # ResizeToFit(1200, 1200, True, (161,175,199, 90))], # format='JPEG', options={'quality': 90}, # upload_to= 'pictures/%Y/%m/%d', height_field="pic_height", width_field="pic_width", # null=True, blank=True, max_length=250, help_text="Picture URL") picture = ProcessedImageField( [ResizeToFit(900, 900, False, (161, 175, 199, 90))], format='JPEG', options={'quality': 90}, upload_to='pictures/%Y/%m/%d', height_field="pic_height", width_field="pic_width", null=True, blank=True, max_length=250, help_text="Picture URL") pic_height = models.IntegerField(null=True, blank=True) pic_width = models.IntegerField(null=True, blank=True) main = models.BooleanField(help_text='Main') visible = models.BooleanField(help_text="Visible") created_at = models.DateTimeField(auto_now_add=True, help_text="URL Created") updated_at = models.DateTimeField(auto_now_add=True, help_text="URL Updated") deleted = models.BooleanField(help_text="Deleted") def save(self): if self.created_at == None: self.created_at = timezone.now() self.updated_at = timezone.now() super(Pictures, self).save() def get_absolute_url(self): return reverse('pictures', kwargs={"pk": self.id}) def __unicode__(self): return unicode(self.title)
class Animal(models.Model): animalId = models.CharField(primary_key=True, max_length=100, blank=True, default=uuid.uuid4) farmId = models.ForeignKey(FarmLocation, on_delete=models.CASCADE) animalName = models.CharField(max_length=200) animalType = models.CharField(max_length=100) animalColor = models.CharField(max_length=20) animalSex = models.CharField(max_length=10) animalAge = models.CharField(max_length=5) animalWeight = models.CharField(max_length=10) thumb = ProcessedImageField(default='media/media/no-image-available.jpg', upload_to='media/', processors=[ResizeToFill(400, 300)], format='JPEG', options={'quality': 70}) timeAdded = models.DateTimeField(auto_now_add=True)
class Itinerary(models.Model): """ Itinerary: stores the sets of trips. """ owner = models.ForeignKey( User, blank=False, null=False, on_delete=models.CASCADE, related_name= 'itineraries', # author.posts: get all posts belong to this user ) title = models.CharField(max_length=100) image = ProcessedImageField(upload_to='itinerary', format='JPEG', options={'quality': 100}, blank=True, null=True, default="default.jpeg") # default pic posted_on = models.DateTimeField( auto_now_add=True, editable=False, ) view = models.BigIntegerField(default=0) is_public = models.BooleanField(default=False) description = models.TextField() like = models.IntegerField(default=0) def __str__(self): return self.title def get_absolute_url(self): return reverse("post", args=[str(self.id)]) def get_like_count(self): return self.likes.count() def get_comment_count(self): return self.comments.count() class Meta: db_table = 'itinerary'
class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) bio = models.CharField(max_length=100, default='', blank=True) website = models.URLField(default='', blank=True) phone = models.CharField(max_length=15, blank=True) # validators should be a list #image = models.ImageField(upload_to='profile_image',default = 'octocat.png') image = ProcessedImageField(default="octocat.png", upload_to='profile_image', processors=[ResizeToFill(200, 200)], format='JPEG', options={'quality': 90}) thumbnail = ImageSpecField(source='image', processors=[ResizeToFill(50, 50)], format='JPEG', options={'quality': 100}) def __str__(self): return self.user.username
class Food(models.Model): """Food Model, managing all kinds of food""" name = models.CharField(db_index=True, unique=True, max_length=100) category = models.ForeignKey(FoodCategory) brief = models.TextField(blank=True) cover_image = ProcessedImageField(upload_to=get_file_path, null=True, blank=True, verbose_name=u'Cover image', processors=[ Adjust(contrast=1.2, sharpness=1.1), SmartResize(300, 300) ], format='JPEG', options={'quality': 90}) storage_time = models.CharField(max_length=50, blank=True) storage_method = models.CharField(max_length=1000, blank=True) like_num = models.IntegerField(default=0) pick_method = models.TextField(blank=True, verbose_name=u'How to choose') food_efficacy = models.TextField(blank=True) view_num = models.IntegerField(default=0) def __unicode__(self): return self.name def active(self): if self.category_id == 1: return False else: return True @models.permalink def get_absolute_url(self): if self.category_id == 1: return None else: return ('food_detail', (), {'pk': self.id}) class Meta: ordering = ['like_num', 'view_num']
class ProjectImage(models.Model): """ Галерея фотографий для проекта строительства """ project = models.ForeignKey(Project, blank=True, null=True, default=None, on_delete=models.CASCADE, related_name="images") alt_desc = models.CharField(verbose_name='alt фотографии', max_length=128, blank=True) image = ProcessedImageField(upload_to=image_upload_to, processors=[ResizeToFill(770, 513)], format='JPEG', options={'quality': 60}) is_active = models.BooleanField(verbose_name='Показывать', default=True) created = models.DateTimeField(auto_now_add=True, auto_now=False) updated = models.DateTimeField(auto_now_add=False, auto_now=True) class Meta: verbose_name = 'Фотография проекта' verbose_name_plural = 'Фотографии проектов'
class Step(models.Model): """docstring for Steps""" recipe = models.ForeignKey(Recipe) step_num = models.PositiveIntegerField() description = models.CharField(max_length=1000) step_image = ProcessedImageField(upload_to=get_file_path, null=True, blank=True, verbose_name=u'Step image', processors=[ Adjust(contrast=1.2, sharpness=1.1), ResizeToFit(width=540, upscale=True) ], format='JPEG', options={'quality': 80}) def __unicode__(self): return u'Step %d of %s' % (self.step_num, self.recipe.name) class Meta: ordering = ['recipe', 'step_num'] unique_together = ("recipe", "step_num")
class UserSettings(models.Model, CroppingModelMixin): LANGUAGE_FINNISH = 'fi' LANGUAGE_SWEDISH = 'sv' LANGUAGE_CHOICES = ( (LANGUAGE_FINNISH, _("suomi")), (LANGUAGE_SWEDISH, _("ruotsi")), ) BOOLEAN_CHOICES = { True: _("Kyllä"), False: _("Ei"), } user = models.OneToOneField(User, related_name='settings') first_name = models.CharField(_("etunimi"), max_length=50) last_name = models.CharField(_("sukunimi"), max_length=50) language = models.CharField(_("kieli"), choices=LANGUAGE_CHOICES, max_length=5, default=LANGUAGE_CHOICES[0][0]) email = models.EmailField(_("sähköposti"), max_length=254, unique=True, blank=True) phone_number = models.CharField(_("puhelinnumero"), max_length=25, null=True, blank=True, default=None) birth_date = models.DateField(_("syntymäaika")) municipality = models.ForeignKey('fimunicipality.Municipality') message_notification = models.BooleanField( _("ilmoitus uusista viesteistä"), default=True) # cropping original_picture = ProcessedImageFieldWithCropping( upload_to=_user_profile_pic_path, processors=[ResizeToFit(width=1280, height=1280, upscale=False)], format='JPEG', options={'quality': 90}, default="") # cropped picture goes here picture = ProcessedImageField( upload_to=_user_profile_pic_path, max_length=120, processors=[ResizeToFit(width=1280, height=1280, upscale=False)], format='JPEG', options={'quality': 90}) picture_medium = ImageSpecField( source='picture', processors=[SmartResize(width=220, height=220)], format='JPEG', options={'quality': 70}) picture_small = ImageSpecField( source='picture', processors=[SmartResize(width=46, height=46)], format='JPEG', options={'quality': 70}) # cropping cropping_pk_field = 'user_id' cropping = ImageRatioField('original_picture', '220x220', size_warning=True, verbose_name=_("Profiilikuvan rajaus")) def get_cropping_cancel_url(self): return reverse('account:profile_picture', kwargs={'user_id': self.user_id}) def get_municipality_display(self): return self.municipality def get_birth_date_display(self): return date(self.birth_date, 'SHORT_DATE_FORMAT') def get_message_notification_display(self): return self.BOOLEAN_CHOICES[self.message_notification] class Meta: verbose_name = _("Käyttäjäasetus") verbose_name_plural = _("Käyttäjäasetukset")
class Project(models.Model): """ Модель проекта строительства со статусом """ DEVELOPMENT = 'разработка' EXPERTISE = 'экспертиза' TENDER = 'аукцион' EXECUTING = 'строительство' FINISHING = 'сдача' PAYMENT = 'выплата' DONE = 'завершен' STATUS_CHOICES = ( (DEVELOPMENT, 'разработка'), (EXPERTISE, 'экспертиза'), (TENDER, 'аукцион'), (EXECUTING, 'строительство'), (FINISHING, 'сдача'), (PAYMENT, 'выплата'), (DONE, 'завершен'), ) name = models.CharField(verbose_name='название', max_length=256, unique=True) slug = models.SlugField(verbose_name='слаг', max_length=128, blank=True) description = models.TextField(verbose_name='описание', blank=True) image = ProcessedImageField(upload_to='projects_images/avatars', processors=[ResizeToFill(530, 530)], format='JPEG', options={'quality': 90}) status = models.CharField(verbose_name='статус', max_length=24, choices=STATUS_CHOICES, blank=True) creation_date = models.DateTimeField(verbose_name='создан', auto_now_add=True, auto_now=False) updated = models.DateTimeField(verbose_name='обновлен', auto_now=True) city = models.CharField(verbose_name='город', max_length=512, blank=True, null=True) address = models.CharField(verbose_name='адрес', max_length=512, blank=True, null=True) coordinate = models.CharField(verbose_name='координаты', max_length=34, null=True, blank=True) map_mark = models.SlugField(verbose_name='id метки на карте', max_length=128, blank=True) text_for_map = models.TextField(verbose_name='текст для метки', max_length=240, null=True, blank=True) is_active = models.BooleanField(verbose_name='активен', default=False) def get_absolute_url(self): return reverse('projects:project', args=[str(self.id)]) def get_absolute_discuss_url(self): return reverse('projects:project_discuss_items', args=[str(self.id)]) def __str__(self): return f"{self.name} ({self.city})" def get_pictures(self): return self.images.select_related() def get_products(self): return self.solutions.select_related() def get_companies(self): return self.companies.select_related() def get_managers(self): return self.managers.select_related() def get_finished_projects(self): pass class Meta: ordering = ('-updated', ) verbose_name = 'Проект' verbose_name_plural = 'Проекты'
class Organization(ActionGeneratingModelMixin, SlugifiedModel, models.Model, CroppingModelMixin): TYPE_UNKNOWN = 0 TYPE_NATION = 1 TYPE_ORGANIZATION = 3 TYPE_MUNICIPALITY = 4 TYPE_SCHOOL = 5 TYPE_OTHER = 10 TYPE_CHOICES = ( (TYPE_UNKNOWN, _("Tuntematon")), (TYPE_NATION, _("Koko Suomi")), (TYPE_ORGANIZATION, _("Järjestö")), (TYPE_MUNICIPALITY, _("Kunta")), (TYPE_SCHOOL, _("Koulu tai muu oppilaitos")), (TYPE_OTHER, _("Muu organisaatio")), ) # these can't be selected for new organizations: MAGIC_TYPES = (TYPE_UNKNOWN, TYPE_NATION) type = models.SmallIntegerField(_("tyyppi"), choices=TYPE_CHOICES) name = MultilingualTextField(_("nimi"), max_length=255, simultaneous_edit=True) description = MultilingualRedactorField(_("kuvaus"), blank=True) municipalities = models.ManyToManyField( 'fimunicipality.Municipality', related_name=_("Kunnat"), verbose_name=_("Valitse kunnat, joiden alueella organisaatio toimii.")) # cropping original_picture = ProcessedImageFieldWithCropping( upload_to=_organization_pic_path, processors=[ResizeToFit(width=1280, height=1280, upscale=False)], format='JPEG', options={'quality': 90}, default="") picture = ProcessedImageField( upload_to=_organization_pic_path, max_length=120, processors=[ResizeToFit(width=1280, height=1280, upscale=False)], format='JPEG', options={'quality': 90}, null=True, default=None, blank=True) picture_medium = ImageSpecField( source='picture', processors=[SmartResize(width=220, height=220)], format='JPEG', options={'quality': 70}) cropping = ImageRatioField('original_picture', '220x220', size_warning=True, verbose_name=_("Profiilikuvan rajaus")) is_active = models.BooleanField(_("aktiivinen"), default=False) created = models.DateTimeField(_("luotu"), default=timezone.now) # TODO: validation: municipality must be unique if type == TYPE_MUNICIPALITY search_text = models.TextField(null=True, default=None) objects = OrganizationQuerySet.as_manager() def __str__(self): return '%s' % self.name def get_cropping_cancel_url(self): return reverse('organization:picture', kwargs={'pk': self.pk}) def absolute_url_viewname(self): return 'organization:detail' def is_real_organization(self): return self.type not in self.MAGIC_TYPES def participates_in_kua(self): if self.type != self.TYPE_MUNICIPALITY: return False try: return bool(self.municipalities.first().kua_participation.pk) except ParticipatingMunicipality.DoesNotExist: return False def description_plaintext(self): desc = '%s' % self.description return bleach.clean(desc.replace('>', '> '), tags=[], strip=True, strip_comments=True).strip() def admins_str(self): admin_list = [a.get_full_name() for a in self.admins.all()] return ", ".join(admin_list) def slugifiable_text(self): return self.name # action processing def action_kwargs_on_create(self): return {'actor': None} def fill_notification_recipients(self, action): for u in User.objects.filter(groups__name=GROUP_NAME_MODERATORS): action.add_notification_recipients(action.ROLE_MODERATOR, u) class Meta: verbose_name = _("organisaatio") verbose_name_plural = _("organisaatiot")
class Idea(ModeratedPolymorphicModelMixIn, Initiative): STATUS_DRAFT = 0 STATUS_PUBLISHED = 3 STATUS_TRANSFERRED = 6 STATUS_DECISION_GIVEN = 9 STATUSES = ( (STATUS_DRAFT, 'created', _("Luonnos")), (STATUS_PUBLISHED, 'published', _("Julkaistu")), (STATUS_TRANSFERRED, 'transferred', _("Viety eteenpäin")), (STATUS_DECISION_GIVEN, 'decision_given', _("Päätös annettu")), ) STATUS_CHOICES = [(s[0], s[2]) for s in STATUSES] # Large picture, to be used as a source for pictures actually displayed on the site picture = ProcessedImageField( upload_to=_idea_main_pic_path, processors=[ResizeToFit(width=1280, height=1280 * 4, upscale=False)], format='JPEG', options={'quality': 90}) # Medium picture, to be displayed on the Idea page, with some effort to make it # fixed width picture_main = ImageSpecField(source='picture', processors=[ ResizeToMinimumDimensions(width=710), ResizeToFit(width=710, height=4 * 710), ], format='JPEG', options={'quality': 70}) # Narrow picture, to be displayed on grids with multiple Ideas, cropping allowed picture_narrow = ImageSpecField(source='picture', processors=[ ResizeToMinimumDimensions(width=350), ResizeToFit(width=350, height=4 * 350) ], format='JPEG', options={'quality': 70}) picture_alt_text = models.CharField( _("Mitä kuvassa on? (kuvaus suositeltava)"), max_length=255, default=None, null=True) status = models.SmallIntegerField(choices=STATUS_CHOICES, default=STATUS_DRAFT) transferred = models.DateTimeField(null=True, default=None, blank=True) decision_given = models.DateTimeField(null=True, default=None, blank=True) votes = GenericRelation("nkvote.Vote", related_query_name="ideas") search_text = models.TextField(default=None, null=True) status_tracker = FieldTracker(fields=['status']) def get_absolute_url(self): return reverse('content:idea_detail', kwargs={'initiative_id': self.pk}) def is_voteable(self): return True @property def popularity(self): return self.comments.all().count() + self.votes.all().count() def is_idea(self): return True def is_voteable(self): return self.status == self.STATUS_PUBLISHED and not self.is_archived() def status_or_visibility(self): if self.visibility == self.VISIBILITY_ARCHIVED: return self.get_visibility_display() return self.get_status_display() def html_allowed(self): return True def get_status_values(self, statuses, status, add_status_value=False): if add_status_value: return tuple(( status, getattr(self, statuses[status][0]), statuses[status][1], )) return tuple(( getattr(self, statuses[status][0]), statuses[status][1], )) def get_kua_statuses_to_status_list(self, add_status_value=False): try: kuas, dupes = [], set() for s in self.kua_initiative.statuses.all(): msg = s.get_friendly_status_message() if msg is not None and not _is_dupe(msg, dupes): if add_status_value: idea_status = get_kua_and_idea_status_map( self, kua_status=s.status) kuas.append(( idea_status, s.created, msg, )) else: kuas.append(( s.created, msg, )) return kuas except KuaInitiative.DoesNotExist: return [] def get_status_list(self, add_status_value=False): statuses = dict([(s[0], (s[1], s[2])) for s in self.STATUSES]) ret = [] if self.status == self.STATUS_DRAFT: ret.append( self.get_status_values(statuses, self.STATUS_DRAFT, add_status_value)) else: ret.append( self.get_status_values(statuses, self.STATUS_PUBLISHED, add_status_value)) kua_status_list = self.get_kua_statuses_to_status_list( add_status_value) if kua_status_list: ret.extend(kua_status_list) elif self.status >= self.STATUS_TRANSFERRED: ret.append( self.get_status_values(statuses, self.STATUS_TRANSFERRED, add_status_value)) if self.status == self.STATUS_DECISION_GIVEN: ret.append( self.get_status_values(statuses, self.STATUS_DECISION_GIVEN, add_status_value)) if self.visibility == self.VISIBILITY_ARCHIVED: visibilities = dict([(s[0], (s[1], s[2])) for s in self.VISIBILITIES]) ret.append( self.get_status_values(visibilities, self.VISIBILITY_ARCHIVED, add_status_value)) return ret def status_complete(self, unnecessary_dates=True): status = self.get_status_list().pop() no_dates = [Idea.VISIBILITY_ARCHIVED] if not unnecessary_dates and self.visibility in no_dates: return status[1] else: status_date = date(status[0], 'SHORT_DATE_FORMAT') return "{}: {}".format(status[1], status_date) def status_complete_fixed_dates(self): return self.status_complete(unnecessary_dates=False) ACTION_SUB_TYPE_IDEA_PUBLISHED = 'idea-published' ACTION_SUB_TYPE_STATUS_UPDATED = 'status-updated' ACTION_SUB_TYPE_STATUS_UPDATED_AFTER_PUBLISH = 'status-updated-after-publish' def check_status_change(self): changed_fields = self.status_tracker.changed() if 'status' in changed_fields: if self.status is not changed_fields['status']: return True return False def get_subtypes(self): subtypes = [] if self.check_status_change(): subtypes.append(Idea.ACTION_SUB_TYPE_STATUS_UPDATED) if self.status == Idea.STATUS_PUBLISHED: subtypes.append(Idea.ACTION_SUB_TYPE_IDEA_PUBLISHED) if self.status > Idea.STATUS_PUBLISHED: subtypes.append( Idea.ACTION_SUB_TYPE_STATUS_UPDATED_AFTER_PUBLISH) return subtypes def create_updated_action(self): return self.check_status_change() def action_kwargs_on_update(self): return { 'actor': None, 'subtype': self.get_subtypes() } # TODO: save modifier def count_survey_submissions(self): count = 0 for s in self.idea_surveys.all(): count += s.count_submissions() return count
class User(Model, AbstractBaseUser): user_id = CharField(max_length=16, null=False, blank=False, unique=True, db_index=True, validators=[RegexValidator(regex=r'^[LA][\d]+$')], verbose_name=_('user ID'), help_text=""" The user ID, which can be either an enrollment ID in the case of a student or a payroll ID in the case of any other person (instructor or staff member). The user ID is protected by a regular expression validator - only valid IDs as specified by the institution's policies is a valid ID. """) date_registered = DateTimeField(auto_now_add=True, null=False, editable=False, verbose_name=_('date registered'), help_text=""" The date in which this user registered. This is kept for historic reasons and is not required by the generator in any way. """) full_name = CharField(max_length=1024, null=False, blank=False, verbose_name=_('full name'), help_text=""" The user's full name. Enough space is given to take into account distinct configurations and most foreign names according to the W3C suggestions on the topic of internationalization and name storage. """) picture = ProcessedImageField(processors=[SmartCrop(128, 128)], upload_to=_upload_to, format='PNG', default='users/default/picture.png', verbose_name=_('user picture'), help_text=""" A picture of the user used for profile only. This is not required by the generator in any way. """) email_address = EmailField(max_length=255, null=False, blank=False, verbose_name=_('email address'), help_text=""" The institutional email address. This is expected to exist and should be provided by the institution through their own internal processes. This serves as the official means for the system to communicate new schedule selection sessions and to notify any mismatch or results. """) role = ForeignKey('rhea.Role', related_name='users', verbose_name=_('user role'), help_text=""" The role, permission-wise, this user has on the system. The role of the user is independent of their user type in order to allow for flexible scenarios, such as instructors belonging to staff as well or students who work on the platform for any given reason. """) permissions = ManyToManyField('rhea.Permission', related_name='+', verbose_name=_('user-specific permissions'), help_text=""" User-wise permissions which can be added or revoked individually. This does not affect the structure of the role's permissions and is only provided for fine-tuning permissions for specific users, such as superusers or specific staff members who require additional privileges with respect to their role. """) objects = UserManager() USERNAME_FIELD = 'user_id' REQUIRED_FIELDS = ['full_name', 'email_address', 'role'] def get_full_name(self): return self.full_name def get_short_name(self): return self.full_name def all_permissions(self): perms = {p for p in self.permissions.all().filter(active=True)} if self.role is not None: perms |= self.role.all_permissions() return perms class Meta(object): verbose_name = _('user') verbose_name_plural = _('users') swappable = 'AUTH_USER_MODEL' app_label = 'rhea'