示例#1
0
class Page(models.Model):
    title = models.CharField(
        max_length=100,
        verbose_name='título',
    )

    summary = models.TextField(verbose_name='resumen para el header', )

    image = models.ImageField(
        max_length=256,
        upload_to='cms/page/',
        verbose_name='imagen del banner principal',
        help_text='El tamano de la imágen debe ser 1920x469px',
    )

    list_image = models.ImageField(
        max_length=256,
        upload_to='cms/page-list/',
        verbose_name='imagen para listados',
        help_text='El tamano de la imágen debe ser 320x410px',
    )

    parent = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        verbose_name='página padre',
        blank=True,
        null=True,
        related_name='children',
    )

    slug = AutoSlugField(
        populate_from='title',
        unique=True,
    )

    position = models.PositiveIntegerField(verbose_name='posición', )

    content = models.TextField(
        verbose_name='contenido de la página',
        blank=True,
    )

    has_children_accordion = models.BooleanField(
        verbose_name='muestra menú lateral',
        default=False,
    )

    show_children = models.BooleanField(
        verbose_name='mostrar hijos',
        default=True,
    )

    footer_title_1 = models.CharField(
        verbose_name='Título 1',
        max_length=100,
        blank=True,
        null=True,
    )

    footer_link_1 = models.CharField(
        verbose_name='Enlace interno 1',
        max_length=256,
        blank=True,
        null=True,
    )

    footer_image_1 = models.ImageField(
        verbose_name='Imagen 1',
        blank=True,
        null=True,
    )

    footer_title_2 = models.CharField(
        verbose_name='Título 2',
        max_length=100,
        blank=True,
        null=True,
    )

    footer_link_2 = models.CharField(
        verbose_name='Enlace interno 2',
        max_length=256,
        blank=True,
        null=True,
    )

    footer_image_2 = models.ImageField(
        verbose_name='Imagen 2',
        blank=True,
        null=True,
    )

    footer_title_3 = models.CharField(
        verbose_name='Título 3',
        max_length=100,
        blank=True,
        null=True,
    )

    footer_link_3 = models.CharField(
        verbose_name='Enlace interno 3',
        max_length=256,
        blank=True,
        null=True,
    )

    footer_image_3 = models.ImageField(
        verbose_name='Imagen 3',
        blank=True,
        null=True,
    )

    meta_title = models.CharField(
        max_length=55,
        help_text='Máximo 55 caracteres',
    )

    meta_description = models.CharField(
        max_length=115,
        help_text='Máximo 115 caracteres',
    )

    created_at = models.DateTimeField(default=timezone.now, )

    updated_at = models.DateTimeField(
        verbose_name='actualizado en',
        default=now,
    )

    is_active = models.BooleanField(
        verbose_name='¿está activa?',
        default=False,
    )

    video_url = models.URLField(
        blank=True,
        verbose_name='url de video de la página',
        help_text='Debe ser un video de youtube. Ej: https://www.youtube.com/'
        'watch?v=697EbMJei_Q',
    )

    @property
    def embed_video_url(self):
        from product.utils import embed_video
        return embed_video(self.video_url)

    def clean(self):
        depth = 0
        parent = self.parent
        while parent is not None:
            depth += 1
            parent = parent.parent
        if depth >= 4:
            raise ValidationError(
                'No se puede crear más de 4 niveles de páginas', )

        if self.show_children and strip_tags(self.content):
            raise ValidationError({
                'content':
                '''Si muestra los hijos no puede tener texto en el
                contenido'''
            })

    @property
    def get_parents(self):
        parents = []
        parent = self.parent
        while parent:
            parents.append(parent)
            parent = parent.parent
        parents.reverse()
        return parents

    @property
    def get_image(self):
        if self.image:
            return self.image
        parent = self.parent
        while parent.image or parent:
            if parent.image:
                return parent.image
            else:
                parent = parent.parent
        return self.parent.image

    def get_absolute_url(self):
        return reverse('cms:page_detail', args=(self.slug, ))

    # Search Mixin
    @property
    def get_search_result_title(self):
        return self.title

    @property
    def get_search_result_description(self):
        return self.summary

    @property
    def get_search_result_image(self):
        return self.list_image or self.image

    @property
    def has_footer(self):
        return (self.footer_title_1 and self.footer_link_1
                and self.footer_image_1 and self.footer_title_2
                and self.footer_link_2 and self.footer_image_2
                and self.footer_title_3 and self.footer_link_3
                and self.footer_image_3)

    def __str__(self):
        return '{0}'.format(self.title)

    class Meta:
        verbose_name = 'página'
        verbose_name_plural = 'páginas'
        unique_together = ('position', 'parent')
        ordering = (
            '-parent_id',
            'position',
        )
示例#2
0
文件: models.py 项目: la-counts/scdc
class Story(models.Model):
    title = models.CharField('Headline', max_length=255)
    slug = AutoSlugField(populate_from='title', unique=True)
    subheader = models.CharField('Subheader', max_length=255, blank=True)
    state = FSMField(default='new',
                     protected=False,
                     db_index=True,
                     state_choices=STATE_CHOICES)
    highlight = models.BooleanField(
        default=False,
        db_index=True,
        help_text='If checked, this story will be listed towards the top')

    organization = models.ForeignKey('datasets.Publisher',
                                     blank=True,
                                     null=True)

    datasets = models.ManyToManyField('datasets.CatalogRecord',
                                      blank=True,
                                      related_name='linked_stories')
    datasource_urls = models.TextField(blank=True,
                                       help_text='''
    Extra datasource urls
    ''')
    #related is spanned through concepts
    #related = models.ManyToManyField('self', blank=True)
    tags = TaggableManager('User tags', blank=True)
    concepts = models.ManyToManyField('focus.Concept', blank=True)
    repostPermissionLine = models.CharField(
        verbose_name='Reposted with Permission Text',
        max_length=255,
        blank=True)

    bodyFeaturedText = RichTextField(verbose_name='Body Featured Text',
                                     blank=True)

    body = RichTextUploadingField()

    author = models.CharField(max_length=255, blank=True)
    posted_by = models.ForeignKey(settings.AUTH_USER_MODEL,
                                  on_delete=models.PROTECT,
                                  related_name='posted_stories')
    approved_by = models.ForeignKey(settings.AUTH_USER_MODEL,
                                    null=True,
                                    blank=True,
                                    related_name='approved_stories')
    published_at = models.DateTimeField(
        null=True, blank=True)  #TODO url based on this or author?
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    featured_image = models.ImageField(upload_to='stories', blank=True)
    card_image = ImageRatioField('featured_image', '400x400')
    wide_card_image = ImageRatioField('featured_image', '800x400')
    featured_image_caption = models.TextField(blank=True)

    objects = StoryManager()

    def __str__(self):
        if len(self.title) > 53:
            return self.title[:50] + '...'
        return self.title

    class Meta:
        verbose_name_plural = 'stories'
        ordering = ['-highlight', '-published_at']

    @property
    def authored_by(self):
        return self.author or self.posted_by

    def get_absolute_url(self):
        return reverse('stories:detail', args=(self.slug, ))

    @transition(field=state, source=['new', 'rejected'], target='published')
    def publish(self):
        if not self.published_at:
            self.published_at = datetime.now()

    @transition(field=state, source='new', target='rejected')
    def reject(self):
        pass

    @lru_cache()
    def related_concepts(self):
        return self.concepts.all().get_descendants(
            include_self=True).search_matched()

    def related_stories(self):
        concepts = self.related_concepts()
        stories = Story.objects.published().filter(
            concepts__in=concepts).exclude(pk=self.pk).distinct()

        return stories

    def related_datasets(self):
        from apps.datasets.models import CatalogRecord
        concepts = self.related_concepts()
        return CatalogRecord.objects.published().filter(concepts__in=concepts)

    def map_datasource_urls(self):
        #TODO datasource is more broad then datasets, handle those as suggestions
        from apps.datasets.models import DatasetURL
        unmapped_urls = list()
        new_datasets = list()
        for ds_url in self.datasource_urls.split():
            if '://' not in ds_url:
                continue
            print('Story datasource:', ds_url)
            du = DatasetURL.objects.get_or_create(url=ds_url)[0]
            if du.catalog_record:
                self.datasets.add(du.catalog_record)
                new_datasets.append(du.catalog_record)
            else:
                cr = du.attempt_catalog_record_sync()
                if cr:
                    self.datasets.add(cr)
                    new_datasets.append(cr)
                else:
                    unmapped_urls.append(du)
        self.datasource_urls = '\n'.join((du.url for du in unmapped_urls))
        self.save()
        return new_datasets
示例#3
0
class FormEntry(models.Model):
    """Form entry."""

    user = models.ForeignKey(AUTH_USER_MODEL,
                             verbose_name=_("User"),
                             on_delete=models.CASCADE)
    name = models.CharField(_("Name"), max_length=255)
    title = models.CharField(_("Title"),
                             max_length=255,
                             null=True,
                             blank=True,
                             help_text=_("Shown in templates if available."))
    slug = AutoSlugField(populate_from='name',
                         verbose_name=_("Slug"),
                         unique=True)
    is_public = models.BooleanField(
        _("Public?"),
        default=False,
        help_text=_("Makes your form visible to the public."))
    active_date_from = models.DateTimeField(
        _("Active from"),
        null=True,
        blank=True,
        help_text=_("Date and time when the form becomes active "
                    "in the format: 'YYYY-MM-DD HH:MM'. "
                    "Leave it blank to activate immediately."))
    active_date_to = models.DateTimeField(
        _("Active until"),
        null=True,
        blank=True,
        help_text=_("Date and time when the form becomes inactive "
                    "in the format: 'YYYY-MM-DD HH:MM'. "
                    "Leave it blank to keep active forever."))
    inactive_page_title = models.CharField(
        _("Inactive form page title"),
        max_length=255,
        null=True,
        blank=True,
        help_text=_("Custom message title to display if form is inactive."))
    inactive_page_message = models.TextField(
        _("Inactive form page body"),
        null=True,
        blank=True,
        help_text=_("Custom message text to display if form is inactive."))
    is_cloneable = models.BooleanField(
        _("Cloneable?"),
        default=False,
        help_text=_("Makes your form cloneable by other users."))
    # position = models.PositiveIntegerField(
    #     _("Position"), null=True, blank=True
    # )
    success_page_title = models.CharField(
        _("Success page title"),
        max_length=255,
        null=True,
        blank=True,
        help_text=_("Custom message title to display after valid form is "
                    "submitted"))
    success_page_message = models.TextField(
        _("Success page body"),
        null=True,
        blank=True,
        help_text=_("Custom message text to display after valid form is "
                    "submitted"))
    action = models.CharField(
        _("Action"),
        max_length=255,
        null=True,
        blank=True,
        help_text=_("Custom form action; don't fill this field, unless really "
                    "necessary."))
    created = models.DateTimeField(_("Created"),
                                   null=True,
                                   blank=True,
                                   auto_now_add=True)
    updated = models.DateTimeField(_("Updated"),
                                   null=True,
                                   blank=True,
                                   auto_now=True)
    is_locked = models.BooleanField(
        _("Locked against editing?"),
        default=False,
        help_text=_("Whether the form entry is locked against editing."),
    )

    class Meta(object):
        """Meta class."""

        verbose_name = _("Form entry")
        verbose_name_plural = _("Form entries")
        unique_together = (
            ('user', 'slug'),
            ('user', 'name'),
        )

    def __str__(self):
        return self.name

    @property
    def is_active(self):
        active_from_ok = True
        active_to_ok = True
        now = timezone.now()

        if self.active_date_from and now < self.active_date_from:
            active_from_ok = False
        if self.active_date_to and now > self.active_date_to:
            active_to_ok = False

        if active_from_ok and active_to_ok:
            return True
        else:
            return False

    def get_absolute_url(self):
        """Get absolute URL.

        Absolute URL, which goes to the form-entry view view page.

        :return string:
        """
        return reverse('fobi.view_form_entry',
                       kwargs={'form_entry_slug': self.slug})
示例#4
0
class Talk(PonyConfModel):

    LICENCES = (
        ('CC-Zero CC-BY', 'CC-Zero CC-BY'),
        ('CC-BY-SA', 'CC-BY-SA'),
        ('CC-BY-ND', 'CC-BY-ND'),
        ('CC-BY-NC', 'CC-BY-NC'),
        ('CC-BY-NC-SA', 'CC-BY-NC-SA'),
        ('CC-BY-NC-ND', 'CC-BY-NC-ND'),
    )

    site = models.ForeignKey(Site, on_delete=models.CASCADE)

    speakers = models.ManyToManyField(Participant, verbose_name=_('Speakers'))
    title = models.CharField(max_length=128, verbose_name=_('Talk Title'))
    slug = AutoSlugField(populate_from='title', unique=True)
    #abstract = models.CharField(max_length=255, blank=True, verbose_name=_('Abstract'))
    description = models.TextField(verbose_name=_('Description of your talk'))
    track = models.ForeignKey(Track,
                              blank=True,
                              null=True,
                              verbose_name=_('Track'))
    notes = models.TextField(
        blank=True,
        verbose_name=_('Message to organizers'),
        help_text=
        _('If you have any constraint or if you have anything that may help you to select your talk, like a video or slides of your talk, please write it down here'
          ))
    category = models.ForeignKey(TalkCategory, verbose_name=_('Talk Category'))
    videotaped = models.BooleanField(_("I'm ok to be recorded on video"),
                                     default=True)
    video_licence = models.CharField(choices=LICENCES,
                                     default='CC-BY-SA',
                                     max_length=10,
                                     verbose_name=_("Video licence"))
    sound = models.BooleanField(_("I need sound"), default=False)
    accepted = models.NullBooleanField(default=None)
    start_date = models.DateTimeField(
        null=True,
        blank=True,
        default=None,
        verbose_name=_('Beginning date and time'))
    duration = models.PositiveIntegerField(default=0,
                                           verbose_name=_('Duration (min)'))
    room = models.ForeignKey(Room, blank=True, null=True, default=None)
    plenary = models.BooleanField(default=False)
    #materials = models.FileField(null=True, upload_to=talk_materials_destination, verbose_name=_('Materials'),
    #                             help_text=_('You can use this field to share some materials related to your intervention.'))

    token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)

    conversation = models.OneToOneField(MessageThread)

    objects = TalkManager()

    class Meta:
        ordering = ('title', )

    def __str__(self):
        return self.title

    def get_speakers_str(self):
        speakers = list(map(str, self.speakers.all()))
        if len(speakers) == 0:
            return 'superman'
        elif len(speakers) == 1:
            return speakers[0]
        else:
            return ', '.join(speakers[:-1]) + ' & ' + str(speakers[-1])

    @property
    def estimated_duration(self):
        return self.duration or self.category.duration

    def get_absolute_url(self):
        return reverse('talk-details', kwargs={'talk_id': self.token})

    @property
    def end_date(self):
        if self.estimated_duration:
            return self.start_date + timedelta(minutes=self.estimated_duration)
        else:
            return None

    @property
    def dtstart(self):
        return self.start_date.strftime('%Y%m%dT%H%M%SZ')

    @property
    def dtend(self):
        return self.end_date.strftime('%Y%m%dT%H%M%SZ')

    @property
    def dtstamp(self):
        return self.updated.strftime('%Y%m%dT%H%M%SZ')

    @property
    def status(self):
        return 'CONFIRMED' if self.accepted else 'TENTATIVE'

    #@property
    #def materials_name(self):
    #    return basename(self.materials.name)

    class Meta:
        ordering = (
            'category__id',
            'title',
        )
示例#5
0
class City(models.Model):
    name = models.CharField('Название города', max_length=100)
    slug = AutoSlugField(unique=True, populate_from='name')

    def __str__(self):
        return self.name
示例#6
0
class Post(models.Model):
    "the class of Post"
    LIVE_STATUS = 1
    DRAFT_STATUS = 2
    HIDDEN_STATUS = 3

    STATUS_CHOICE = (
        (LIVE_STATUS, 'Live'),
        (DRAFT_STATUS, 'Draft'),
        (HIDDEN_STATUS, 'Hidden'),
    )
    title = models.CharField(max_length=255,
                             unique=True,
                             help_text=_('Title of the post.'))
    #slug = models.SlugField(unique_for_date="created_on",unique=True,help_text=_('Short title used in the URLs.'))
    slug = AutoSlugField(populate_from=lambda instance: instance.title,
                         unique_with=['author__name', 'date_published__month'],
                         slugify=lambda value: value.replace(' ', '-'),
                         always_update=True)
    #slug=AutoSlugField(unique=True,always_update=True)
    author = models.ForeignKey(User)
    status = models.IntegerField(_('status'),
                                 choices=STATUS_CHOICE,
                                 help_text=_('The status of this news.'))
    category = models.ForeignKey(Category)

    img = models.ImageField('Image',
                            upload_to='upload-img',
                            blank=True,
                            null=True)
    #tag=models.ManyToManyField(Tag,blank=True,null=True,help_text=_('Tags for the post.'))
    tags = TaggableManager(
        through=TagPost,
        blank=True,
        help_text=_('Tags for the post.Support only En-lang'))
    content = models.TextField(blank=False)
    hoter = models.IntegerField(blank=True, default=0)
    #  comments = models.ForeignKey(Comment,blank=True,null=True)

    created_on = models.DateTimeField(auto_now_add=True, editable=False)
    date_modified = models.DateTimeField(auto_now_add=True, editable=False)
    date_published = models.DateTimeField(auto_now_add=True, editable=False)

    #tags_list=[]

    # manager

    #live = LivePostManager()
    live = LivePostManager()
    objects = models.Manager()

    class Meta:
        ordering = ['-created_on']

    def save(self, *args, **kwargs):

        import datetime
        #pdb.set_trace()

        #from blogserver.middleware import threadlocals
        #if threadlocals.get_current_user():
        #    self.author = threadlocals.get_current_user()
        #self.
        self.created_on = datetime.datetime.now()
        super(Post, self).save()
        #pdb.set_trace()

    @models.permalink
    def get_absolute_url(self):
        return ('post_detail', (), {
            'year': self.date_published.strftime("%Y"),
            'month': self.date_published.strftime("%m").lower(),
            'day': self.date_published.strftime("%d"),
            'slug': self.slug
        })

    def __unicode__(self):
        return self.title

    def serialize_fields(self):
        """Only these fields will be included in API responses."""
        return [
            'id', 'title', 'slug', 'status', 'author', 'tag', 'category',
            'img', 'created_on', 'date_published', 'date_modified', 'content',
            'comments', 'hoter'
        ]
示例#7
0
class Article(mixins.Seo):
    title = models.CharField(_('title'), max_length=255)
    slug = AutoSlugField(_('slug'),
                         populate_from='title',
                         unique_with='category',
                         editable=True,
                         blank=True)
    published = models.BooleanField(_('published'), default=True)
    order = models.PositiveSmallIntegerField(_('order'), default=0)
    language = LanguageField(_('language'))
    category = models.ForeignKey(Category,
                                 verbose_name=_('category'),
                                 related_name='_articles',
                                 null=True,
                                 blank=True)
    intro = models.TextField(_('intro'), blank=True, default='')
    content = models.TextField(_('content'), blank=True, default='')
    thumbnail = FileBrowseField(_('thumbnail'),
                                help_text=_('Displayed in category view.'),
                                max_length=255,
                                null=True,
                                blank=True)

    show_created = models.BooleanField(_('show creation date'), default=False)
    show_breadcrumbs = models.BooleanField(_('show breadcrumbs'), default=True)
    show_back_link = models.BooleanField(_('show back link'), default=True)
    images_thumbnails_size = FilebrowserVersionField(
        _('images thumbnails size'), allow_null=True)

    created = models.DateTimeField(_('creation date'), default=timezone.now)
    last_modified = models.DateTimeField(_('last modification date'),
                                         auto_now=True)

    class Meta:
        app_label = 'content'
        ordering = ('language', 'category', 'order', 'title')
        verbose_name = _('article')
        verbose_name_plural = _('articles')

    def __str__(self):
        return self.title

    @property
    def route(self):
        if not self.published:
            return None
        category = self.category
        if category:
            if category.route:
                if category.route == '/':
                    category_route = ''
                else:
                    category_route = category.route
                return '{}/{}'.format(category_route, self.slug)
        page = self.pages.filter(published=True).first()
        if page:
            return page.route
        return None

    @property
    def all_routes(self):
        if not self.published:
            return []
        routes = []
        if self.route:
            routes.append(self.route)
        for page in self.pages.filter(published=True):
            if page.route not in routes:
                routes.append(page.route)
        return routes

    @property
    def meta(self):
        return utils.generate_meta(
            title=self.meta_title_override or self.title,
            title_suffix=self.meta_title_site_name_suffix,
            description=self.meta_description_override,
            robots=self.meta_robots_override)

    @property
    def breadcrumbs(self):
        output = [{'name': self.title, 'route': self.route}]
        parent = self.category
        while parent:
            if parent.route:
                output.append({'name': parent.name, 'route': parent.route})
            parent = parent.parent
        return reversed(output)

    @property
    def back_link(self):
        try:
            return self.category.route or '/'
        except AttributeError:
            return '/'

    @property
    def thumbnail_or_first_image(self):
        try:
            return self.thumbnail or self.images.filter(published=True) \
                .first().image
        except AttributeError:
            return None
示例#8
0
class FormatVersion(VersionedModel, models.Model):
    """ Format that a tool identifies. """

    uuid = UUIDField(editable=False,
                     unique=True,
                     version=4,
                     help_text=_("Unique identifier"))
    format = models.ForeignKey(
        "Format",
        to_field="uuid",
        related_name="version_set",
        null=True,
        verbose_name=_("the related format"),
        on_delete=models.CASCADE,
    )
    version = models.CharField(_("version"),
                               max_length=10,
                               null=True,
                               blank=True)
    pronom_id = models.CharField(_("pronom id"),
                                 max_length=32,
                                 null=True,
                                 blank=True)
    description = models.CharField(
        _("description"),
        max_length=128,
        null=True,
        blank=True,
        help_text=_("Formal name to go in the METS file."),
    )
    access_format = models.BooleanField(_("access format"), default=False)
    preservation_format = models.BooleanField(_("preservation format"),
                                              default=False)

    slug = AutoSlugField(populate_from="description",
                         unique_with="format",
                         always_update=True)

    class Meta:
        verbose_name = _("Format version")
        ordering = ["format", "description"]

    def validate_unique(self, *args, **kwargs):
        super(FormatVersion, self).validate_unique(*args, **kwargs)

        if len(self.pronom_id) > 0:
            qs = self.__class__._default_manager.filter(
                pronom_id=self.pronom_id, enabled=1)

            if not self._state.adding and self.pk is not None:
                qs = qs.exclude(pk=self.pk)

            if qs.exists():
                raise ValidationError({
                    NON_FIELD_ERRORS: [
                        _("Unable to save, an active Format Version  with this pronom id already exists."
                          )
                    ]
                })

    def __unicode__(self):
        return _("%(format)s: %(description)s (%(pronom_id)s)") % {
            "format": self.format,
            "description": self.description,
            "pronom_id": self.pronom_id,
        }
示例#9
0
class Institution(models.Model):
    name = models.CharField(
        verbose_name='Name',
        max_length=255,
    )
    slug = AutoSlugField(populate_from='name')
    abbreviation = models.CharField(
        verbose_name='Abbreviation',
        max_length=255,
    )
    url = models.URLField(
        verbose_name='Url',
        blank=True,
    )
    year = models.PositiveIntegerField(_('Year'),
                                       default=get_default_year_now,
                                       validators=[MaxValueValidator(4000)])
    country = models.ForeignKey(
        Country,
        blank=True,
        null=True,
        on_delete=models.PROTECT,
    )
    contact_person = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name='institution_contact',
        verbose_name='Contact person',
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )
    memorandum = models.BooleanField(verbose_name='Memorandum signed',
                                     default=False)
    logo = models.ForeignKey('wagtailimages.Image',
                             verbose_name='Logo',
                             null=True,
                             blank=True,
                             on_delete=models.SET_NULL,
                             related_name='+')

    panels = [
        FieldPanel('name'),
        # FieldPanel('slug'),
        FieldPanel('abbreviation'),
        FieldPanel('url'),
        FieldPanel('year'),
        FieldPanel('country'),
        FieldPanel('contact_person'),
        FieldPanel('memorandum'),
        ImageChooserPanel('logo'),
    ]

    search_fields = [
        index.SearchField('name'),
        index.SearchField('abbreviation'),
        index.SearchField('content'),
        index.FilterField('year'),
        index.FilterField('contact_person'),
        index.FilterField('country'),
    ]

    class Meta:
        verbose_name = _('Institution')
        verbose_name_plural = _('Institutions')
        ordering = ['name']
        unique_together = ('name', 'abbreviation', 'country')

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('institutions:detail', args=[self.slug])
示例#10
0
class DiscussionTopic(models.Model):
    """
    A topic (thread) associated with a CourseOffering
    """
    offering = models.ForeignKey(CourseOffering,
                                 null=False,
                                 on_delete=models.PROTECT)
    title = models.CharField(max_length=140,
                             help_text="A brief description of the topic")
    content = models.TextField(help_text='The inital message for the topic.')
    created_at = models.DateTimeField(auto_now_add=True)
    last_activity_at = models.DateTimeField(auto_now_add=True)
    message_count = models.IntegerField(default=0)
    status = models.CharField(
        max_length=3,
        choices=TOPIC_STATUSES,
        default='OPN',
        help_text=
        "The topic status: Closed: no replies allowed, Hidden: cannot be seen")
    pinned = models.BooleanField(
        default=False,
        help_text="Should this topic be pinned to bring attention?")
    author = models.ForeignKey(Member, on_delete=models.PROTECT)

    def autoslug(self):
        return make_slug(self.title)

    slug = AutoSlugField(populate_from='autoslug',
                         null=False,
                         editable=False,
                         unique_with=['offering'])
    config = JSONField(null=False, blank=False, default=dict)
    # p.config['markup']:  markup language used: see courselib/markup.py
    # p.config['math']: content uses MathJax? (boolean)
    # p.config['brushes']: used SyntaxHighlighter brushes (list of strings) -- no longer used with highlight.js

    defaults = {
        'markup': 'creole',
        'math': False,
    }
    markup, set_markup = getter_setter('markup')
    math, set_math = getter_setter('math')

    def save(self, *args, **kwargs):
        if self.status not in [status[0] for status in TOPIC_STATUSES]:
            raise ValueError('Invalid topic status')

        self.content = ensure_sanitary_markup(self.content,
                                              self.markup(),
                                              restricted=True)

        new_topic = self.id is None
        super(DiscussionTopic, self).save(*args, **kwargs)

        # handle subscriptions
        if new_topic:
            subs = DiscussionSubscription.objects.filter(
                member__offering=self.offering).select_related(
                    'member__person')
            for s in subs:
                s.notify(self)

    def get_absolute_url(self):
        return reverse('offering:discussion:view_topic',
                       kwargs={
                           'course_slug': self.offering.slug,
                           'topic_slug': self.slug
                       })

    def new_message_update(self):
        self.last_activity_at = datetime.datetime.now()
        self.message_count = self.message_count + 1
        self.save()

    def last_activity_at_delta(self):
        return _time_delta_to_string(self.last_activity_at)

    def created_at_delta(self):
        return _time_delta_to_string(self.created_at)

    def __str___(self):
        return self.title

    def html_content(self):
        "Convert self.content to HTML"
        return markup_to_html(self.content,
                              self.markup(),
                              offering=self.offering,
                              html_already_safe=True,
                              restricted=True)

    def still_editable(self):
        td = datetime.datetime.now() - self.created_at
        seconds = td.days * 86400 + td.seconds
        return seconds <= 120

    def editable_time_left(self):
        td = datetime.datetime.now() - self.created_at
        seconds = td.days * 86400 + td.seconds
        if seconds > 120:
            return 'none'
        minutes, seconds = divmod(120 - seconds, 60)
        return "%dm:%ds" % (minutes, seconds)

    def exportable(self):
        """
        Create JSON-serializable representation of topic, for export.
        """
        data = {
            'title': self.title,
            'body': self.content,
            'created_at': self.created_at.isoformat(),
            'author': self.author.person.userid,
            'status': self.status,
            'pinned': self.pinned
        }
        messages = DiscussionMessage.objects.filter(
            topic=self).select_related('author__person')
        data['replies'] = [m.exportable() for m in messages]
        return data
示例#11
0
class Player(models.Model):
    name = models.CharField(max_length=200, unique=True)
    dota_mmr = models.PositiveIntegerField()
    dota_id = models.CharField(max_length=200, null=True, blank=True)
    discord_id = models.CharField(max_length=200, null=True, blank=True)
    slug = AutoSlugField(populate_from='name')

    ladder_mmr = models.PositiveIntegerField(default=0)
    score = models.PositiveIntegerField(default=0)
    rank_ladder_mmr = models.PositiveIntegerField(default=0)
    rank_score = models.PositiveIntegerField(default=0)

    voice_issues = models.BooleanField(default=False)
    bot_access = models.BooleanField(default=False)
    vouched = models.BooleanField(default=False)
    blacklist = models.ManyToManyField('self',
                                       symmetrical=False,
                                       related_name='blacklisted_by')

    # boundaries for ladder mmr
    min_allowed_mmr = models.PositiveIntegerField(default=0)
    max_allowed_mmr = models.PositiveIntegerField(default=0)

    # ban levels
    BAN_PLAYING = 1
    BAN_PLAYING_AND_LOBBY = 2
    BAN_CHOICES = (
        (None, "Not banned"),
        (BAN_PLAYING, 'Banned from playing only'),
        (BAN_PLAYING_AND_LOBBY, 'Banned from playing and lobby'),
    )
    banned = models.PositiveSmallIntegerField(choices=BAN_CHOICES,
                                              null=True,
                                              blank=True)

    new_reg_pings = models.BooleanField(default=False)
    queue_afk_ping = models.BooleanField(default=True)

    description = models.CharField(max_length=200, null=True, blank=True)
    vouch_info = models.CharField(max_length=200, null=True, blank=True)

    roles = AutoOneToOneField(RolesPreference)

    objects = PlayerManager()

    class Meta:
        ordering = ['rank_ladder_mmr']

    def __str__(self):
        return '%s' % self.name

    def save(self, *args, **kwargs):
        # TODO: Move this to clean_fields() later
        # TODO: (can't do it atm, because of empty dota_id in test data).
        # TODO: Or even better move this to manager.update_scores()
        # TODO  (this will allow us bulk_update in future)
        self.score = max(self.score or 0, 0)
        self.ladder_mmr = max(self.ladder_mmr or 0, 0)

        created = not self.pk
        if created:
            self.roles = RolesPreference.objects.create()

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

        # give player initial score and mmr
        if created:
            PlayerManager.init_score(self, reset_mmr=True)
示例#12
0
class DiscussionMessage(models.Model):
    """
    A message (post) associated with a Discussion Topic
    """
    topic = models.ForeignKey(DiscussionTopic, on_delete=models.CASCADE)
    content = models.TextField(blank=False)
    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=3,
                              choices=MESSAGE_STATUSES,
                              default='VIS')
    author = models.ForeignKey(Member, on_delete=models.PROTECT)

    def autoslug(self):
        return make_slug(self.author.person.userid)

    slug = AutoSlugField(populate_from='autoslug',
                         null=False,
                         editable=False,
                         unique_with=['topic'])
    config = JSONField(null=False, blank=False, default=dict)
    # p.config['markup']:  markup language used: see courselib/markup.py
    # p.config['math']: content uses MathJax? (boolean)
    # p.config['brushes']: used SyntaxHighlighter brushes (list of strings) -- no longer used with highlight.js

    defaults = {'math': False, 'markup': 'creole'}
    math, set_math = getter_setter('math')
    markup, set_markup = getter_setter('markup')

    #brushes, set_brushes = getter_setter('brushes')

    def save(self, *args, **kwargs):
        if self.status not in [status[0] for status in MESSAGE_STATUSES]:
            raise ValueError('Invalid topic status')
        if not self.pk:
            self.topic.new_message_update()

        self.content = ensure_sanitary_markup(self.content,
                                              self.markup(),
                                              restricted=True)

        new_message = self.id is None
        super(DiscussionMessage, self).save(*args, **kwargs)

        # handle subscriptions
        if new_message:
            subs = DiscussionSubscription.objects.filter(
                member__offering=self.topic.offering).select_related(
                    'member__person')
            for s in subs:
                s.notify_message(self)

    def html_content(self):
        "Convert self.content to HTML"
        return markup_to_html(self.content,
                              self.markup(),
                              offering=self.topic.offering,
                              html_already_safe=True,
                              restricted=True)

    def get_absolute_url(self):
        return self.topic.get_absolute_url() + '#reply-' + str(self.id)

    def create_at_delta(self):
        return _time_delta_to_string(self.created_at)

    def still_editable(self):
        td = datetime.datetime.now() - self.created_at
        seconds = td.days * 86400 + td.seconds
        return seconds <= 120

    def editable_time_left(self):
        td = datetime.datetime.now() - self.created_at
        seconds = td.days * 86400 + td.seconds
        if seconds > 120:
            return 'none'
        minutes, seconds = divmod(120 - seconds, 60)
        return "%dm:%ds" % (minutes, seconds)

    def was_edited(self):
        td = self.modified_at - self.created_at
        return self.modified_at > self.created_at and td > datetime.timedelta(
            seconds=3) and self.status != 'HID'

    def exportable(self):
        """
        Create JSON-serializable representation of message, for export.
        """
        data = {
            'body': self.content,
            'created_at': self.created_at.isoformat(),
            'author': self.author.person.userid,
            'status': self.status
        }
        return data

    def to_dict(self):
        return {
            "author": self.author.person.userid,
            "topic": self.topic.title,
            "content": self.content,
            "visibility": self.get_status_display(),
            "created": str(self.created_at),
            "modified": str(self.modified_at)
        }
示例#13
0
class Communication(models.Model):
    title = models.CharField(
        max_length=100,
        verbose_name='título',
    )

    category = models.ForeignKey(
        'cms.CommunicationCategory',
        on_delete=models.CASCADE,
        verbose_name='categoría',
    )

    image = models.ImageField(
        max_length=256,
        upload_to='cms/communication/',
        verbose_name='imagen del banner principal',
        help_text='La imagen debe tener el tamaño de 1920x469px',
    )

    list_image = models.ImageField(
        max_length=256,
        upload_to='cms/communication-list/',
        verbose_name='imagen para listados',
        help_text='El tamano de la imágen debe ser 520x680',
    )

    slug = AutoSlugField(
        populate_from='title',
        unique=True,
    )

    content = models.TextField(verbose_name='contenido de la página', )

    extended_content = models.TextField(
        verbose_name='contenido extendido de la página',
        blank=True,
    )

    event_date = models.DateField(
        verbose_name='fecha de inicio',
        blank=True,
        null=True,
    )

    end_date = models.DateField(
        verbose_name='fecha de finalización',
        blank=True,
        null=True,
    )

    event_hour = models.CharField(
        max_length=15,
        verbose_name='hora del evento',
        blank=True,
        null=True,
    )

    event_city = models.CharField(max_length=100,
                                  verbose_name='sitio del evento',
                                  blank=True,
                                  null=True)

    event_place = models.CharField(
        max_length=100,
        verbose_name='lugar del evento',
        blank=True,
        null=True,
    )

    footer_title_1 = models.CharField(
        verbose_name='Título 1',
        max_length=100,
        blank=True,
        null=True,
    )

    footer_link_1 = models.CharField(
        verbose_name='Enlace interno 1',
        max_length=256,
        blank=True,
        null=True,
    )

    footer_image_1 = models.ImageField(
        verbose_name='Imagen 1',
        blank=True,
        null=True,
        help_text='El tamano de la imágen debe ser 991x122',
    )

    footer_title_2 = models.CharField(
        verbose_name='Título 2',
        max_length=100,
        blank=True,
        null=True,
    )

    footer_link_2 = models.CharField(
        verbose_name='Enlace interno 2',
        max_length=256,
        blank=True,
        null=True,
    )

    footer_image_2 = models.ImageField(
        verbose_name='Imagen 2',
        blank=True,
        null=True,
        help_text='El tamano de la imágen debe ser 991x122',
    )

    footer_title_3 = models.CharField(
        verbose_name='Título 3',
        max_length=100,
        blank=True,
        null=True,
    )

    footer_link_3 = models.CharField(
        verbose_name='Enlace interno 3',
        max_length=256,
        blank=True,
        null=True,
    )

    footer_image_3 = models.ImageField(
        verbose_name='Imagen 3',
        blank=True,
        null=True,
        help_text='El tamano de la imágen debe ser 991x122',
    )

    meta_title = models.CharField(
        max_length=55,
        help_text='Máximo 55 caracteres',
    )

    meta_description = models.CharField(
        max_length=115,
        help_text='Máximo 115 caracteres',
    )

    is_active = models.BooleanField(
        verbose_name='está activo',
        default=True,
    )

    has_contact = models.BooleanField(
        verbose_name='¿tiene contacto?',
        default=False,
    )

    created_at = models.DateTimeField(default=timezone.now, )

    updated_at = models.DateTimeField(
        verbose_name='actualizado en',
        default=now,
    )

    def get_absolute_url(self):
        return reverse('cms:communication_detail', args=(self.slug, ))

    # Search Mixin
    @property
    def get_search_result_title(self):
        return self.title

    @property
    def get_search_result_description(self):
        return self.content

    @property
    def get_search_result_image(self):
        return self.list_image or self.image

    @property
    def has_footer(self):
        return (self.footer_title_1 and self.footer_link_1
                and self.footer_image_1 and self.footer_title_2
                and self.footer_link_2 and self.footer_image_2
                and self.footer_title_3 and self.footer_link_3
                and self.footer_image_3)

    def __str__(self):
        return '{0}'.format(self.title)

    class Meta:
        verbose_name = 'comunicación'
        verbose_name_plural = 'comunicaciones'
        ordering = ('-created_at', )
示例#14
0
class CommunicationCategory(models.Model):
    title = models.CharField(
        max_length=100,
        verbose_name='título',
    )

    slug = AutoSlugField(
        populate_from='title',
        unique=True,
    )

    position = models.PositiveIntegerField(
        verbose_name='posición',
        unique=True,
    )

    has_comments = models.BooleanField(
        verbose_name='Comentarios habilitados',
        default=False,
    )

    has_share_buttons = models.BooleanField(
        verbose_name='Botones de compartir habilitados',
        default=False,
    )

    created_at = models.DateTimeField(default=timezone.now,
                                      verbose_name='Fecha de creación')

    footer_title_1 = models.CharField(
        verbose_name='Título 1',
        max_length=100,
        blank=True,
        null=True,
    )

    footer_link_1 = models.CharField(
        verbose_name='Enlace interno 1',
        max_length=256,
        blank=True,
        null=True,
    )

    footer_image_1 = models.ImageField(
        verbose_name='Imagen 1',
        blank=True,
        null=True,
    )

    footer_title_2 = models.CharField(
        verbose_name='Título 2',
        max_length=100,
        blank=True,
        null=True,
    )

    footer_link_2 = models.CharField(
        verbose_name='Enlace interno 2',
        max_length=256,
        blank=True,
        null=True,
    )

    footer_image_2 = models.ImageField(
        verbose_name='Imagen 2',
        blank=True,
        null=True,
    )

    footer_title_3 = models.CharField(
        verbose_name='Título 3',
        max_length=100,
        blank=True,
        null=True,
    )

    footer_link_3 = models.CharField(
        verbose_name='Enlace interno 3',
        max_length=256,
        blank=True,
        null=True,
    )

    footer_image_3 = models.ImageField(
        verbose_name='Imagen 3',
        blank=True,
        null=True,
    )

    @property
    def has_footer(self):
        return (self.footer_title_1 and self.footer_link_1
                and self.footer_image_1 and self.footer_title_2
                and self.footer_link_2 and self.footer_image_2
                and self.footer_title_3 and self.footer_link_3
                and self.footer_image_3)

    def get_absolute_url(self):
        return reverse('cms:communication_by_category_list',
                       args=(self.slug, ))

    def __str__(self):
        return '{0}'.format(self.title)

    class Meta:
        verbose_name = 'categoría de comunicación'
        verbose_name_plural = 'categorías de comunicación'
        ordering = ('position', )

    def clean(self):
        if self.title:
            unique_case_insensitive(self, 'title')
示例#15
0
文件: models.py 项目: xhacker/coursys
class RAAppointment(models.Model):
    """
    This stores information about a (Research Assistant)s application and pay.
    """
    person = models.ForeignKey(Person,
                               help_text='The RA who is being appointed.',
                               null=False,
                               blank=False,
                               related_name='ra_person')
    sin = models.PositiveIntegerField(null=True, blank=True)
    hiring_faculty = models.ForeignKey(
        Person,
        help_text='The manager who is hiring the RA.',
        related_name='ra_hiring_faculty')
    unit = models.ForeignKey(Unit,
                             help_text='The unit that owns the appointment',
                             null=False,
                             blank=False)
    hiring_category = models.CharField(max_length=4,
                                       choices=HIRING_CATEGORY_CHOICES,
                                       default='GRA')
    scholarship = models.ForeignKey(
        Scholarship,
        null=True,
        blank=True,
        help_text='Scholarship associated with this appointment. Optional.')
    project = models.ForeignKey(Project, null=False, blank=False)
    account = models.ForeignKey(Account, null=False, blank=False)
    start_date = models.DateField(auto_now=False, auto_now_add=False)
    end_date = models.DateField(auto_now=False, auto_now_add=False)
    pay_frequency = models.CharField(max_length=60,
                                     choices=PAY_FREQUENCY_CHOICES,
                                     default='B')
    lump_sum_pay = models.DecimalField(max_digits=8,
                                       decimal_places=2,
                                       verbose_name="Total Pay")
    lump_sum_hours = models.DecimalField(max_digits=8,
                                         decimal_places=2,
                                         verbose_name="Total Hours",
                                         blank=True,
                                         null=True)
    biweekly_pay = models.DecimalField(max_digits=8, decimal_places=2)
    pay_periods = models.DecimalField(max_digits=6, decimal_places=1)
    hourly_pay = models.DecimalField(max_digits=8, decimal_places=2)
    hours = models.DecimalField(max_digits=5,
                                decimal_places=2,
                                verbose_name="Biweekly Hours")
    reappointment = models.BooleanField(
        default=False, help_text="Are we re-appointing to the same position?")
    medical_benefits = models.BooleanField(
        default=False, help_text="50% of Medical Service Plan")
    dental_benefits = models.BooleanField(default=False,
                                          help_text="50% of Dental Plan")
    notes = models.TextField(
        blank=True,
        help_text=
        "Biweekly emplyment earnings rates must include vacation pay, hourly rates will automatically have vacation pay added. The employer cost of statutory benefits will be charged to the amount to the earnings rate."
    )
    comments = models.TextField(blank=True, help_text="For internal use")
    offer_letter_text = models.TextField(
        null=True,
        help_text=
        "Text of the offer letter to be signed by the RA and supervisor.")

    def autoslug(self):
        if self.person.userid:
            ident = self.person.userid
        else:
            ident = unicode(self.person.emplid)
        return make_slug(self.unit.label + '-' +
                         unicode(self.start_date.year) + '-' + ident)

    slug = AutoSlugField(populate_from=autoslug,
                         null=False,
                         editable=False,
                         unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    deleted = models.BooleanField(null=False, default=False)
    config = JSONField(null=False, blank=False,
                       default={})  # addition configuration stuff
    defaults = {'use_hourly': False}
    use_hourly, set_use_hourly = getter_setter('use_hourly')

    def __unicode__(self):
        return unicode(self.person) + "@" + unicode(self.created_at)

    class Meta:
        ordering = ['person', 'created_at']

    def save(self, *args, **kwargs):
        # set SIN field on the Person object
        if self.sin and 'sin' not in self.person.config:
            self.person.set_sin(self.sin)
            self.person.save()
        super(RAAppointment, self).save(*args, **kwargs)

    def default_letter_text(self):
        """
        Default text for the letter (for editing, or use if not set)
        """
        substitutions = {
            'start_date': self.start_date.strftime("%B %d, %Y"),
            'end_date': self.end_date.strftime("%B %d, %Y"),
            'lump_sum_pay': self.lump_sum_pay,
            'biweekly_pay': self.biweekly_pay,
        }
        if self.pay_frequency == 'B':
            text = DEFAULT_LETTER_BIWEEKLY
        else:
            text = DEFAULT_LETTER_LUMPSUM
        return '\n\n'.join(text) % substitutions

    def letter_paragraphs(self):
        """
        Return list of paragraphs in the letter (for PDF creation)
        """
        text = self.offer_letter_text or self.default_letter_text()
        text = normalize_newlines(text)
        return text.split("\n\n")

    @classmethod
    def semester_guess(cls, date):
        """
        Guess the semester for a date, in the way that financial people do (without regard to class start/end dates)
        """
        mo = date.month
        if mo <= 4:
            se = 1
        elif mo <= 8:
            se = 4
        else:
            se = 7
        semname = str((date.year - 1900) * 10 + se)
        return Semester.objects.get(name=semname)

    @classmethod
    def start_end_dates(cls, semester):
        """
        First and last days of the semester, in the way that financial people do (without regard to class start/end dates)
        """
        return Semester.start_end_dates(semester)
        #yr = int(semester.name[0:3]) + 1900
        #sm = int(semester.name[3])
        #if sm == 1:
        #    start = datetime.date(yr, 1, 1)
        #    end = datetime.date(yr, 4, 30)
        #elif sm == 4:
        #    start = datetime.date(yr, 5, 1)
        #    end = datetime.date(yr, 8, 31)
        #elif sm == 7:
        #    start = datetime.date(yr, 9, 1)
        #    end = datetime.date(yr, 12, 31)
        #return start, end

    def start_semester(self):
        "Guess the starting semester of this appointment"
        start_semester = RAAppointment.semester_guess(self.start_date)
        # We do this to eliminate hang - if you're starting N days before
        # semester 1134, you aren't splitting that payment across 2 semesters.
        start, end = RAAppointment.start_end_dates(start_semester)
        if end - self.start_date < datetime.timedelta(SEMESTER_SLIDE):
            return start_semester.next_semester()
        return start_semester

    def end_semester(self):
        "Guess the ending semester of this appointment"
        end_semester = RAAppointment.semester_guess(self.end_date)
        # We do this to eliminate hang - if you're starting N days after
        # semester 1134, you aren't splitting that payment across 2 semesters.
        start, end = RAAppointment.start_end_dates(end_semester)
        if self.end_date - start < datetime.timedelta(SEMESTER_SLIDE):
            return end_semester.previous_semester()
        return end_semester

    def semester_length(self):
        "The number of semesters this contracts lasts for"
        return self.end_semester() - self.start_semester() + 1
示例#16
0
class Repository(Resource, EntityUrlMixin):
    """Repository."""
    ENTITY_TYPES = ()
    LODS = ()

    translatable_fields = (
        ("access_conditions", "Access Conditions", "TODO: Help text"),
        ("buildings", "Buildings", "TODO: Help text"),
        ("collecting_policies", "Collecting Policies", "TODO: Help text"),
        ("dates_of_existence", "Dates of Existence", "TODO: Help text"),
        ("disabled_access", "Disabled Access", "TODO: Help text"),
        ("finding_aids", "Finding Aids", "TODO: Help text"),
        ("functions", "Functions", "TODO: Help text"),
        ("general_context", "General Context", "TODO: Help text"),
        ("geocultural_context", "Geocultural Context", "TODO: Help text"),
        ("history", "History", "TODO: Help text"),
        ("holdings", "Holdings", "TODO: Help text"),
        ("internal_structures", "Internal Structures", "TODO: Help text"),
        ("legal_status", "Legal Status", "TODO: Help text"),
        ("maintenance_notes", "Maintenance Notes", "TODO: Help text"),
        ("mandates", "Mandates", "TODO: Help text"),
        ("opening_times", "Opening Times", "TODO: Help text"),
        ("places", "Places", "TODO: Help text"),
        ("reproduction_services", "Reproduction Services", "TODO: Help text"),
        ("research_services", "Research Services", "TODO: Help text"),
        ("rules", "Rules", "TODO: Help text"),
        ("sources", "Sources", "TODO: Help text"),
    )

    name = models.CharField("Authorized Form of Name", max_length=255)
    slug = AutoSlugField(populate_from="name", unique=True)
    identifier = models.CharField(max_length=255)
    lod = models.CharField("Level of Description",
                           max_length=255,
                           choices=LODS,
                           blank=True,
                           null=True)
    type_of_entity = models.CharField("Type of Entity",
                                      max_length=255,
                                      choices=ENTITY_TYPES,
                                      blank=True,
                                      null=True)
    logo = ImageWithThumbsField("Logo",
                                null=True,
                                blank=True,
                                upload_to=lambda inst, fn: os.path.join(
                                    inst.slug, "%s_logo%s" %
                                    (inst.slug, os.path.splitext(fn)[1])),
                                sizes=settings.THUMBNAIL_SIZES)
    languages = jsonfield.JSONField(_("Language(s)"),
                                    blank=True,
                                    default=EMPTY_JSON_LIST)
    scripts = jsonfield.JSONField(_("Script(s)"),
                                  blank=True,
                                  default=EMPTY_JSON_LIST)
    tags = TaggableManager(blank=True)
    objects = RepositoryManager()

    class Meta:
        verbose_name_plural = "repositories"

    def natural_key(self):
        return (self.slug, )

    @property
    def primary_contact(self):
        """Get the main contact property."""
        try:
            return self.contact_set.all().order_by("primary")[0]
        except IndexError:
            return None

    @property
    def country_code(self):
        contact = self.primary_contact
        if contact:
            return contact.country_code

    @property
    def country(self):
        contact = self.primary_contact
        if contact and contact.country_code:
            return utils.country_name_from_code(contact.country_code)

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self.slug)

    def __unicode__(self):
        return self.name
示例#17
0
class Talk(PonyConfModel):
    LICENCES = (
        ('CC-Zero CC-BY', 'CC-Zero CC-BY'),
        ('CC-BY-SA', 'CC-BY-SA'),
        ('CC-BY-ND', 'CC-BY-ND'),
        ('CC-BY-NC', 'CC-BY-NC'),
        ('CC-BY-NC-SA', 'CC-BY-NC-SA'),
        ('CC-BY-NC-ND', 'CC-BY-NC-ND'),
    )

    site = models.ForeignKey(Site, on_delete=models.CASCADE)
    speakers = models.ManyToManyField(Participant, verbose_name=_('Speakers'))
    title = models.CharField(max_length=128, verbose_name=_('Talk Title'))
    slug = AutoSlugField(populate_from='title', unique=True)
    description = models.TextField(
        verbose_name=_('Description of your talk'),
        help_text=_('This description will be visible on the program.'))
    track = models.ForeignKey(Track,
                              blank=True,
                              null=True,
                              verbose_name=_('Track'),
                              on_delete=models.SET_NULL)
    tags = models.ManyToManyField(Tag, blank=True)
    notes = models.TextField(
        blank=True,
        verbose_name=_('Message to organizers'),
        help_text=_(
            'If you have any constraint or if you have anything that may '
            'help you to select your talk, like a video or slides of your'
            ' talk, please write it down here. This field will only be '
            'visible by organizers.'))
    category = models.ForeignKey(TalkCategory,
                                 verbose_name=_('Talk Category'),
                                 on_delete=models.PROTECT)
    videotaped = models.BooleanField(_("I'm ok to be recorded on video"),
                                     default=True)
    video_licence = models.CharField(choices=LICENCES,
                                     default='CC-BY-SA',
                                     max_length=32,
                                     verbose_name=_("Video licence"))
    sound = models.BooleanField(_("I need sound"), default=False)
    accepted = models.BooleanField(null=True, default=None)
    confirmed = models.BooleanField(null=True, default=None)
    start_date = models.DateTimeField(
        null=True,
        blank=True,
        default=None,
        verbose_name=_('Beginning date and time'))
    duration = models.PositiveIntegerField(default=0,
                                           verbose_name=_('Duration (min)'))
    room = models.ForeignKey(Room,
                             blank=True,
                             null=True,
                             default=None,
                             on_delete=models.SET_NULL)
    plenary = models.BooleanField(default=False)
    materials = models.FileField(
        null=True,
        blank=True,
        upload_to=talks_materials_destination,
        verbose_name=_('Materials'),
        help_text=
        _('You can use this field to share some materials related to your intervention.'
          ))
    video = models.URLField(max_length=1000,
                            blank=True,
                            default='',
                            verbose_name='Video URL')
    token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    conversation = models.OneToOneField(MessageThread,
                                        on_delete=models.PROTECT)

    objects = TalkManager()

    class Meta:
        ordering = ('title', )

    def __str__(self):
        return self.title

    def get_speakers_str(self):
        speakers = list(map(str, self.speakers.all()))
        if len(speakers) == 0:
            return 'superman'
        elif len(speakers) == 1:
            return speakers[0]
        else:
            return ', '.join(speakers[:-1]) + ' & ' + str(speakers[-1])

    def get_status_str(self):
        if self.accepted is True:
            if self.confirmed is True:
                return _('Confirmed')
            elif self.confirmed is False:
                return _('Cancelled')
            else:
                return _('Waiting confirmation')
        elif self.accepted is False:
            return _('Refused')
        else:
            return _('Pending decision, score: %(score).1f') % {
                'score': self.score
            }

    def get_status_color(self):
        if self.accepted is True:
            if self.confirmed is True:
                return 'success'
            elif self.confirmed is False:
                return 'danger'
            else:
                return 'info'
        elif self.accepted is False:
            return 'default'
        else:
            return 'warning'

    def get_tags_html(self):
        return mark_safe(' '.join(map(lambda tag: tag.link, self.tags.all())))

    def get_csv_row(self):
        return [
            self.pk,
            self.title,
            self.description,
            self.category,
            self.track,
            [speaker.pk for speaker in self.speakers.all()],
            [speaker.name for speaker in self.speakers.all()],
            [tag.name for tag in self.tags.all()],
            1 if self.videotaped else 0,
            self.video_licence,
            1 if self.sound else 0,
            self.estimated_duration,
            self.room,
            1 if self.plenary else 0,
            self.materials,
            self.video,
        ]

    @property
    def estimated_duration(self):
        return self.duration or self.category.duration

    def get_absolute_url(self):
        return reverse('talk-details', kwargs={'talk_id': self.pk})

    @property
    def end_date(self):
        if self.estimated_duration:
            return self.start_date + timedelta(minutes=self.estimated_duration)
        else:
            return None

    @property
    def materials_name(self):
        return basename(self.materials.name)

    class Meta:
        ordering = (
            'category__id',
            'title',
        )
示例#18
0
class Collection(Resource, EntityUrlMixin):
    """Model representing an archival description."""
    COLLECTION, FONDS, SUBFONDS, SERIES, SUBSERIES, FILE, ITEM = range(7)
    LODS = (
        (COLLECTION, _("Collection")),
        (FONDS, _("Fonds")),
        (SUBFONDS, _("Sub-fonds")),
        (SERIES, _("Series")),
        (SUBSERIES, _("Sub-series")),
        (FILE, _("File")),
        (ITEM, _("Item")),
    )

    translatable_fields = (
        ("access_conditions", "Access Conditions", "TODO: Help text"),
        ("accruals", "Accruals", "TODO: Help text"),
        ("acquisition", "Immediate source of acquisition or transfer",
         "TODO: Help text"),
        ("alternate_title", "Alternate Title", "TODO: Help text"),
        ("appraisal", "Appraisal", "TODO: Help text"),
        ("archival_history", "Archival History", "TODO: Help text"),
        ("arrangement", "Arrangement", "TODO: Help text"),
        ("edition", "Edition", "TODO: Help text"),
        ("extent_and_medium", "Extent and Medium", "TODO: Help text"),
        ("finding_aids", "Finding Aids", "TODO: Help text"),
        ("institution_responsible_identifier",
         "Institution Responsible Identifier", "TODO: Help text"),
        ("location_of_copies", "Location of Copies", "TODO: Help text"),
        ("location_of_originals", "Location of Originals", "TODO: Help text"),
        ("notes", _("Notes"), "TODO: Help text"),
        ("physical_characteristics", "Physical Characteristics",
         "TODO: Help text"),
        ("related_units_of_description", "Related Units of Description",
         "TODO: Help text"),
        ("reproduction_conditions", "Reproduction Conditions",
         "TODO: Help text"),
        ("revision_history", "Revision History", "TODO: Help text"),
        ("rules", "Rules", "TODO: Help text"),
        ("scope_and_content", "Scope and Content", "TODO: Help text"),
        ("sources", "Sources", "TODO: Help text"),
    )

    name = models.CharField("Title", max_length=255)
    slug = AutoSlugField(populate_from="name", unique=True)
    identifier = models.CharField(max_length=255)
    lod = models.PositiveIntegerField(_("Level of Description"),
                                      choices=LODS,
                                      blank=True,
                                      null=True)
    creator = models.ForeignKey("Authority", null=True, blank=True)
    repository = models.ForeignKey(Repository)
    languages = jsonfield.JSONField(_("Language(s) of Materials"),
                                    blank=True,
                                    default=EMPTY_JSON_LIST)
    scripts = jsonfield.JSONField(_("Script(s) of Materials"),
                                  blank=True,
                                  default=EMPTY_JSON_LIST)
    languages_of_description = jsonfield.JSONField(
        _("Language(s) of Description"), blank=True, default=EMPTY_JSON_LIST)
    scripts_of_description = jsonfield.JSONField(_("Script(s) of Description"),
                                                 blank=True,
                                                 default=EMPTY_JSON_LIST)

    tags = TaggableManager(blank=True)
    objects = CollectionManager()

    class Meta:
        verbose_name_plural = "collections"
        unique_together = (("identifier", "repository"), )

    def natural_key(self):
        return (self.slug, )

    @property
    def start_date(self):
        """Shortcut for getting the earliest date to which
        this collection relates."""
        try:
            fdate = self.date_set.all().order_by("start_date")[0]
        except IndexError:
            return
        return fdate.start_date

    @property
    def end_date(self):
        """Shortcut for getting the lastest date to which
        this collection relates."""
        try:
            edate = self.date_set.all().order_by("-end_date", "-start_date")[0]
        except IndexError:
            return
        if edate.end_date:
            return edate.end_date
        return edate.start_date

    @property
    def date(self):
        """Average of start/end dates. Not exact."""
        if not self.end_date and not self.start_date:
            return
        if not self.end_date:
            return self.start_date
        return self.start_date + ((self.end_date - self.start_date) / 2)

    @property
    def date_range(self):
        """List of years this collection covers."""
        if not self.end_date and not self.start_date:
            return []
        if not self.end_date:
            return [self.start_date]
        return [datetime.date(y,1,1) for y in \
                range(self.start_date.year, self.end_date.year + 1)]

    @property
    def date_range_string(self):
        """List of years this collection covers."""
        dates = self.date_range
        if not dates:
            return None
        if len(dates) == 1:
            return str(self.start_date.year)
        return "%s-%s" % (dates[0].year, dates[-1].year)

    @property
    def name_access(self):
        # TODO: Find the proper way of doing this
        return [ne.subject for ne in NameAccess.objects.select_related()\
                    .filter(object_id=self.id).all()]

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self.slug)

    def __unicode__(self):
        return self.name
示例#19
0
class Link(models.Model):
    "A specific link within a topic."
    url = models.URLField()
    summary = models.CharField(max_length=255)
    slug = AutoSlugField(populate_from='summary', unique=True, max_length=255)
    text = models.TextField(u'Description')
    user = models.ForeignKey(User, related_name="added_links")
    topic = models.ForeignKey(Topic)
    created_on = models.DateTimeField(auto_now_add=1)
    liked_by = models.ManyToManyField(User, related_name="liked_links")
    disliked_by = models.ManyToManyField(User, related_name="disliked_links")
    liked_by_count = models.IntegerField(default=0)
    disliked_by_count = models.IntegerField(default=0)
    points = models.DecimalField(default=0, max_digits=7, decimal_places=2)
    recommended_done = models.BooleanField(default=False)
    #
    related_links_calculated = models.BooleanField(default=False)

    objects = LinkManager()
    """The Voting algo:
    On each upvote increase the points by min(voter.karma, 10)
    On each upvote decrease the points by min(voter.karma, 10)
    increase/decrease the voters karma by 1
    """
    def upvote(self, user):
        return self.vote(user, True)

    def downvote(self, user):
        return self.vote(user, False)

    def vote(self, user, direction=True):

        "Vote the given link either up or down, using a user. Calling multiple times with same user must have no effect."
        #Check if the current user can vote this, link or raise exception
        if self.topic.permissions == 'Public':
            pass  #Anyone can vote
        else:
            try:
                subscribed_user = SubscribedUser.objects.get(topic=self.topic,
                                                             user=user)
            except SubscribedUser.DoesNotExist:
                raise CanNotVote(
                    'The topic %s is non-public, and you are not subscribed to it.'
                    % self.topic.name)
        vote, created, flipped = LinkVote.objects.do_vote(user=user,
                                                          link=self,
                                                          direction=direction)
        save_vote = False
        profile = user.get_profile()
        change = max(0, min(defaults.MAX_CHANGE_PER_VOTE, profile.karma))
        if created and direction:
            self.liked_by_count += 1
            self.points += change
            save_vote = True
            profile = self.user.get_profile()
            profile.karma += defaults.CREATORS_KARMA_PER_VOTE
            # print self.user, user, profile.karma

        if created and not direction:
            self.disliked_by_count += 1
            self.points -= change
            save_vote = True
            profile = self.user.get_profile()
            profile.karma -= defaults.CREATORS_KARMA_PER_VOTE

        if direction and flipped:
            #Upvoted and Earlier downvoted
            self.liked_by_count += 1
            self.disliked_by_count -= 1
            self.points += 2 * change
            save_vote = True
            profile = self.user.get_profile()
            profile.karma += 2 * defaults.CREATORS_KARMA_PER_VOTE

        if not direction and flipped:
            #downvoted and Earlier upvoted
            self.liked_by_count -= 1
            self.disliked_by_count += 1
            self.points -= 2 * change
            save_vote = True
            profile = self.user.get_profile()
            profile.karma -= 2 * defaults.CREATORS_KARMA_PER_VOTE
        if not user == self.user:
            profile.save()
        if save_vote:
            self.save()
        return vote

    def reset_vote(self, user):
        "Reset a previously made vote"
        try:
            vote = LinkVote.objects.get(link=self, user=user)
        except LinkVote.DoesNotExist, e:
            "trying to reset vote, which does not exist."
            return
        change = max(
            0, min(defaults.MAX_CHANGE_PER_VOTE,
                   user.get_profile().karma))
        if vote.direction:
            self.liked_by_count -= 1
            self.points -= change
            self.save()
            profile = self.user.get_profile()
            profile.karma -= defaults.CREATORS_KARMA_PER_VOTE
        if not vote.direction:
            self.points += change
            self.disliked_by_count -= 1
            self.save()
            profile = self.user.get_profile()
            profile.karma += defaults.CREATORS_KARMA_PER_VOTE
        if not user == self.user:
            profile.save()
        vote.delete()
        return vote
示例#20
0
class Authority(Resource, EntityUrlMixin):
    """Model representing an archival authority."""
    FULL, PARTIAL, MINIMAL = range(3)
    CORP, FAMILY, PERSON = range(3)

    LODS = (
        (FULL, _("Full")),
        (PARTIAL, _("Partial")),
        (MINIMAL, _("Minimal")),
    )
    ENTITY_TYPES = (
        (CORP, _("Corporate Body")),
        (FAMILY, _("Family")),
        (PERSON, _("Person")),
    )

    translatable_fields = (
        ("dates_of_existence", "Dates of Existence", "TODO: Help text"),
        ("functions", "Functions", "TODO: Help text"),
        ("general_context", "General Context", "TODO: Help text"),
        ("history", "History", "TODO: Help text"),
        ("institution_responsible_identifier",
         "Institution Responsible Identifier", "TODO: Help text"),
        ("internal_structures", "Internal Structures", "TODO: Help text"),
        ("legal_status", "Legal Status", "TODO: Help text"),
        ("mandates", "Mandates", "TODO: Help text"),
        ("places", "Places", "TODO: Help text"),
        ("revision_history", "Revision History", "TODO: Help text"),
        ("sources", "Sources", "TODO: Help text"),
    )

    name = models.CharField("Authorized Form of Name", max_length=255)
    slug = AutoSlugField(populate_from="name", unique=True)
    identifier = models.CharField(max_length=255)
    lod = models.PositiveIntegerField(_("Level of Description"),
                                      choices=LODS,
                                      blank=True,
                                      null=True)
    type_of_entity = models.PositiveIntegerField(_("Type of Entity"),
                                                 choices=ENTITY_TYPES,
                                                 blank=True,
                                                 null=True)
    languages = jsonfield.JSONField(_("Language(s)"),
                                    blank=True,
                                    default=EMPTY_JSON_LIST)
    scripts = jsonfield.JSONField(_("Script(s)"),
                                  blank=True,
                                  default=EMPTY_JSON_LIST)
    tags = TaggableManager(blank=True)
    objects = AuthorityManager()

    class Meta:
        verbose_name_plural = "authorities"

    def natural_key(self):
        return (self.slug, )

    @property
    def type_name(self):
        return self.ENTITY_TYPES[self.type_of_entity][1]

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self.slug)

    def __unicode__(self):
        return self.name
示例#21
0
class Category(mixins.Seo):
    name = models.CharField(_('name'), max_length=255)
    slug = AutoSlugField(_('slug'),
                         populate_from='name',
                         unique_with='parent',
                         editable=True,
                         blank=True)
    published = models.BooleanField(_('published'), default=True)
    order = models.PositiveSmallIntegerField(_('order'), default=0)
    language = LanguageField(_('language'))
    parent = models.ForeignKey('self',
                               verbose_name=_('parent'),
                               related_name='_subcategories',
                               null=True,
                               blank=True)
    description = models.TextField(_('description'), blank=True, default='')
    image = FileBrowseField(_('image'), max_length=255, null=True, blank=True)

    # category view - general
    show_description = models.BooleanField(_('show description'), default=True)
    show_image = models.BooleanField(_('show image'), default=True)
    image_size = FilebrowserVersionField(_('image size'))
    show_breadcrumbs = models.BooleanField(_('show bradcrumbs'), default=True)
    show_back_link = models.BooleanField(_('show back link'), default=True)

    # category view - subcategories
    show_subcategories_descriptions = models.BooleanField(
        _('show descriptions'), default=True)
    show_subcategories_images = models.BooleanField(_('show images'),
                                                    default=True)
    subcategories_images_size = FilebrowserVersionField(_('images size'))

    # category view - articles
    show_articles_intros = models.BooleanField(_('show intros'), default=True)
    show_articles_contents = models.BooleanField(_('show contents'),
                                                 default=False)
    show_articles_created = models.BooleanField(_('show creation dates'),
                                                default=False)
    show_articles_images = models.BooleanField(_('show images'), default=True)
    articles_images_size = FilebrowserVersionField(_('images size'))

    pagination = models.BooleanField(_('pagination'), default=True)
    articles_on_page = models.PositiveSmallIntegerField(_('articles on page'),
                                                        default=10)

    # article view
    av_articles_images_size = FilebrowserVersionField(_('images size'),
                                                      allow_null=True)

    class Meta:
        app_label = 'content'
        ordering = ('language', 'order', 'name')
        verbose_name = _('category')
        verbose_name_plural = _('categories')

    def __str__(self):
        return self.name

    @property
    def route(self):
        if not self.published:
            return None

        def try_parents():
            tree = []
            parent = self.parent
            while (parent):
                current = parent
                if not current.published:
                    return None
                tree.append(current)
                parent = current.parent
            tree.reverse()
            tree.append(self)

            # root category must be published as page
            page = tree[0].pages.filter(published=True).first()
            if not page:
                return None

            if page.route == '/':
                path = ''
            else:
                path = page.route
            rest = '/'.join([c.slug for c in tree[1:]])
            if rest:
                path += '/{}'.format(rest)
            return path

        def try_page():
            page = self.pages.filter(published=True).first()
            if page:
                return page.route

        return try_parents() or try_page()

    @property
    def all_routes(self):
        if not self.published:
            return []
        routes = []
        if self.route:
            routes.append(self.route)
        for page in self.pages.filter(published=True):
            if page.route not in routes:
                routes.append(page.route)
        return routes

    @property
    def meta(self):
        return utils.generate_meta(
            title=self.meta_title_override or self.name,
            title_suffix=self.meta_title_site_name_suffix,
            description=self.meta_description_override,
            robots=self.meta_robots_override)

    @property
    def breadcrumbs(self):
        output = [{'name': self.name, 'route': self.route}]
        parent = self.parent
        while parent:
            output.append({'name': parent.name, 'route': parent.route})
            parent = parent.parent
        return reversed(output)

    @property
    def back_link(self):
        try:
            return self.parent.route
        except AttributeError:
            return '/'

    @property
    def subcategories(self):
        return self._subcategories.filter(published=True,
                                          language__in=utils.served_langs())

    @property
    def articles(self):
        return self._articles.filter(published=True,
                                     language__in=utils.served_langs())

    def get_image(self, request):
        version = self.image_size
        try:
            return request.build_absolute_uri(
                self.image.original.version_generate(version).url)
        except (AttributeError, OSError):
            return None

    def get_subcategory_image(self, category, request):
        try:
            image = category.image.original
        except AttributeError:
            return None
        version = self.subcategories_images_size
        try:
            return request.build_absolute_uri(
                image.version_generate(version).url)
        except OSError:
            return None

    def get_article_image(self, article, request):
        try:
            image = article.thumbnail_or_first_image.original
        except AttributeError:
            return None
        version = self.articles_images_size
        try:
            return request.build_absolute_uri(
                image.version_generate(version).url)
        except OSError:
            return None
示例#22
0
class Sermon(models.Model):
    title = models.CharField(max_length=120,
                             help_text='The title of the sermon.')
    slug = AutoSlugField(populate_from='title', unique=True)
    date = models.DateField(help_text='The date the sermon was preached.')
    series = models.ForeignKey(SermonSeries,
                               blank=True,
                               null=True,
                               help_text=('What series the sermon is from, if '
                                          'any - you can add a series using '
                                          '<a href="/manage/sermons/series/'
                                          'create/">this form</a>.'))
    speaker = models.ForeignKey(SermonSpeaker,
                                help_text=('You can add a speaker using '
                                           '<a href="/manage/sermons/speaker/'
                                           'create/">this form</a>.'))
    passage = BiblePassageField(blank=True,
                                null=True,
                                help_text=('NB. This doesn\'t currently '
                                           'support multiple passages.'))
    mp3 = models.FileField(blank=True,
                           null=True,
                           upload_to='kanisa/sermons/mp3s/%Y/',
                           max_length=200,
                           verbose_name='MP3',
                           help_text=('The MP3 will automatically have ID3 '
                                      'data filled in (e.g. title, genre).'))
    details = models.TextField(blank=True,
                               null=True,
                               help_text=('e.g. What themes does the sermon '
                                          'cover?'))
    transcript = models.TextField(blank=True,
                                  null=True,
                                  help_text=('For audio-impaired users - as '
                                             'close to a verbatim transcript '
                                             'as possible.'))
    downloads = models.IntegerField(default=0, editable=False)
    podcast_downloads = models.IntegerField(default=0, editable=False)
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    objects = SermonManager()
    basic_objects = models.Manager()
    preached_objects = PreachedSermonManager()

    def __unicode__(self):
        return self.title

    def url(self):
        # I'm not sure this belongs here, but I don't really like
        # having to switch on whether or not a sermon is part of a
        # series when I need the URL for a sermon. I'll revisit this
        # later.
        if not self.series:
            return reverse('kanisa_public_standalone_sermon_detail',
                           args=[
                               self.slug,
                           ])

        return reverse('kanisa_public_sermon_detail',
                       args=[
                           self.series.slug,
                           self.slug,
                       ])

    def mp3_url(self):
        if not self.mp3:
            return None

        return self.mp3.url

    def in_the_future(self):
        return self.date > date.today()

    def delete(self, using=None):
        if self.mp3:
            os.remove(self.mp3.file.name)

        return super(Sermon, self).delete(using)

    class Meta:
        # Need this because I've split up models.py into multiple
        # files.
        app_label = 'kanisa'
        ordering = ('-date', )
示例#23
0
class Transient(BaseModel):

    ### Entity relationships ###
    # Required
    status = models.ForeignKey(TransientStatus,
                               models.SET(get_sentinel_transientstatus))
    obs_group = models.ForeignKey(ObservationGroup, on_delete=models.CASCADE)

    # Optional
    non_detect_band = models.ForeignKey(PhotometricBand,
                                        related_name='+',
                                        null=True,
                                        blank=True,
                                        on_delete=models.SET_NULL)
    best_spec_class = models.ForeignKey(TransientClass,
                                        related_name='+',
                                        null=True,
                                        blank=True,
                                        on_delete=models.SET_NULL)
    photo_class = models.ForeignKey(TransientClass,
                                    related_name='+',
                                    null=True,
                                    blank=True,
                                    on_delete=models.SET_NULL)
    best_spectrum = models.ForeignKey('TransientSpectrum',
                                      related_name='+',
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL)
    host = models.ForeignKey(Host,
                             null=True,
                             blank=True,
                             on_delete=models.SET_NULL)
    abs_mag_peak_band = models.ForeignKey(PhotometricBand,
                                          related_name='+',
                                          null=True,
                                          blank=True,
                                          on_delete=models.SET_NULL)
    antares_classification = models.ForeignKey(AntaresClassification,
                                               null=True,
                                               blank=True,
                                               on_delete=models.SET_NULL)
    internal_survey = models.ForeignKey(InternalSurvey,
                                        null=True,
                                        blank=True,
                                        on_delete=models.SET_NULL)
    tags = models.ManyToManyField(TransientTag, blank=True)

    ### Properties ###
    # Required
    name = models.CharField(max_length=64)
    ra = models.FloatField()
    dec = models.FloatField()

    # Optional
    disc_date = models.DateTimeField(null=True, blank=True)
    candidate_hosts = models.TextField(
        null=True, blank=True
    )  # A string field to hold n hosts -- if we don't quite know which is the correct one
    redshift = models.FloatField(null=True, blank=True)
    redshift_err = models.FloatField(null=True, blank=True)
    redshift_source = models.CharField(max_length=64, null=True, blank=True)
    non_detect_date = models.DateTimeField(null=True, blank=True)
    non_detect_limit = models.FloatField(null=True, blank=True)
    mw_ebv = models.FloatField(null=True, blank=True)
    abs_mag_peak = models.FloatField(null=True, blank=True)
    abs_mag_peak_date = models.DateTimeField(null=True, blank=True)
    postage_stamp_file = models.CharField(max_length=512,
                                          null=True,
                                          blank=True)
    k2_validated = models.NullBooleanField(null=True, blank=True)
    k2_msg = models.TextField(null=True, blank=True)
    TNS_spec_class = models.CharField(
        max_length=64, null=True, blank=True
    )  # To hold the TNS classiciation in case we don't have a matching enum
    point_source_probability = models.FloatField(null=True, blank=True)

    slug = AutoSlugField(null=True,
                         default=None,
                         unique=True,
                         populate_from='name')

    def CoordString(self):
        return GetSexigesimalString(self.ra, self.dec)

    def RADecimalString(self):
        return '%.7f' % (self.ra)

    def DecDecimalString(self):
        return '%.7f' % (self.dec)

    def Separation(self):
        host = Host.objects.get(pk=self.host_id)
        return '%.2f' % getSeparation(self.ra, self.dec, host.ra, host.dec)

    def modified_date_pacific(self):
        date_format = '%m/%d/%Y %H:%M:%S %Z'
        mod_date = self.modified_date.astimezone(timezone('US/Pacific'))
        return mod_date.strftime(date_format)

    def disc_date_string(self):
        date_format = '%m/%d/%Y'
        return self.disc_date.strftime(date_format)

    def disc_mag(self):

        transient_query = Q(transient=self.id)
        all_phot = yse_models.TransientPhotometry.objects.filter(
            transient_query)
        phot_ids = all_phot.values('id')
        phot_data_query = Q(photometry__id__in=phot_ids)
        disc_query = Q(discovery_point=1)
        disc_mag = yse_models.TransientPhotData.objects.exclude(
            data_quality__isnull=False).filter(phot_data_query & disc_query)
        if len(disc_mag):
            return disc_mag[0].mag
        else:
            return None

    def recent_mag(self):
        date_format = '%m/%d/%Y'

        transient_query = Q(transient=self.id)
        all_phot = yse_models.TransientPhotometry.objects.filter(
            transient_query)
        phot_ids = all_phot.values('id')
        phot_data_query = Q(photometry__id__in=phot_ids)
        recent_mag = yse_models.TransientPhotData.objects.exclude(
            data_quality__isnull=False).filter(phot_data_query).order_by(
                '-obs_date')

        if len(recent_mag):
            return '%.2f' % (recent_mag[0].mag)
        else:
            return None

    def recent_magdate(self):
        date_format = '%m/%d/%Y'

        transient_query = Q(transient=self.id)
        all_phot = yse_models.TransientPhotometry.objects.filter(
            transient_query)
        phot_ids = all_phot.values('id')
        phot_data_query = Q(photometry__id__in=phot_ids)
        recent_mag = yse_models.TransientPhotData.objects.exclude(
            data_quality__isnull=False).filter(phot_data_query).order_by(
                '-obs_date')

        if len(recent_mag):
            return '%s' % (recent_mag[0].obs_date.strftime(date_format))
        else:
            return None

    def z_or_hostz(self):
        if self.redshift:
            return self.redshift
        elif self.host and self.host.redshift:
            return self.host.redshift
        else:
            return None

    def name_table_sort(self):
        if len(self.name) > 4:
            addnums = 7 - len(self.name)
            sortname = "".join(
                [self.name[:4], "".join(['1'] * addnums), self.name[4:]])

            return sortname
        else:
            return None

    def __str__(self):
        return self.name

    def natural_key(self):
        return self.name
示例#24
0
class SermonSeries(models.Model):
    title = models.CharField(max_length=120,
                             help_text='The name of the series.')
    slug = AutoSlugField(populate_from='title', unique=True)
    image = ImageField(
        null=True,
        blank=True,
        upload_to='kanisa/sermons/series/',
        help_text=('This will be used in most places where the series is '
                   'shown on the site. Must be at least 400px by 300px.'))
    intro = models.TextField(
        blank=True,
        null=True,
        help_text=('Sum up this series in a few sentences. In some places '
                   'this may be displayed without the details section '
                   'below.'))
    details = models.TextField(
        blank=True,
        null=True,
        help_text=('e.g. What themes does the series cover?'))
    active = models.BooleanField(default=True,
                                 help_text='Is this series currently ongoing?')
    passage = BiblePassageField(
        blank=True,
        null=True,
        help_text=('NB. This doesn\'t currently support multiple passages.'))
    modified = models.DateTimeField(auto_now=True)

    objects = SermonSeriesManager()

    def __unicode__(self):
        return self.title

    def num_sermons(self):
        return self.the_num_sermons

    def sermons(self):
        return self.sermon_set.order_by('date').all()

    def date_range(self):
        """Returns a (first_date, last_date) tuple representing the
        date of the first sermon and the date of the last sermon. If
        the series is currently active (implying the last sermon has
        not yet been added to the series), the second element in the
        tuple will be None. If the series has no sermons, None will be
        returned.

        """
        sermons = Sermon.basic_objects.filter(series=self)
        sermons = list(sermons.order_by('date').only('date'))

        if not sermons:
            return None

        first_sermon = sermons[0]
        if self.active:
            return (first_sermon.date, None)

        last_sermon = sermons[-1]
        return (first_sermon.date, last_sermon.date)

    def image_or_default(self):
        if self.image:
            return self.image

        branding = BrandingInformation('apple')
        return branding.url

    class Meta:
        # Need this because I've split up models.py into multiple
        # files.
        app_label = 'kanisa'
        ordering = ('-active', )
        verbose_name_plural = 'Sermon series'
        permissions = (('manage_sermons', 'Can manage your sermons'), )
示例#25
0
class Ad(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    author = models.ForeignKey('auth.User',
                               on_delete=models.CASCADE,
                               default='rohanjha')

    product_name = models.CharField(max_length=260)
    slug = AutoSlugField(populate_from='product_name')
    price = models.DecimalField(max_digits=15, decimal_places=2, default=0.0)

    discount_amount = models.IntegerField(blank=True, null=True, default=0)

    description = models.TextField()
    product_brand = models.CharField(max_length=250, default='None')
    sizes_available = models.CharField(max_length=200, default='XL L M')

    stock = models.IntegerField(max_length=100,
                                default=1,
                                blank=True,
                                null=True)

    product_pic0 = models.ImageField(blank=True,
                                     null=True,
                                     upload_to='images/%Y/%m/%d/')
    product_pic1 = models.ImageField(blank=True,
                                     null=True,
                                     upload_to='images/%Y/%m/%d/')
    product_pic2 = models.ImageField(blank=True,
                                     null=True,
                                     upload_to='images/%Y/%m/%d/')
    product_pic3 = models.ImageField(blank=True,
                                     null=True,
                                     upload_to='images/%Y/%m/%d/')

    featured = models.BooleanField(default=False)
    active = models.BooleanField(default=True)
    category = models.ForeignKey('ShopApp.Category',
                                 related_name='advertisements',
                                 on_delete=models.CASCADE)
    subcategory = models.ForeignKey('ShopApp.SubCategory',
                                    related_name='advertisements',
                                    on_delete=models.CASCADE)
    published_date = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.product_name

    def get_absolute_url(self):
        return reverse("ad_detail", kwargs={"pk": self.pk})

    def save(self):
        if (self.product_pic0):
            self.product_pic0 = self.ModifyImage(self.product_pic0)
        if (self.product_pic1):
            self.product_pic1 = self.ModifyImage(self.product_pic1)
        if (self.product_pic2):
            self.product_pic2 = self.ModifyImage(self.product_pic2)
        if (self.product_pic3):
            self.product_pic3 = self.ModifyImage(self.product_pic3)
        super().save()

    def ModifyImage(self, product_pic):
        im = Image.open(product_pic)
        output = BytesIO()
        im = im.resize((600, 380), PIL.Image.ANTIALIAS)
        im.save(output, format='JPEG', quality=80)
        output.seek(0)
        product_pic = InMemoryUploadedFile(
            output, 'ImageField', "%s.jpg" % product_pic.name.split('.')[0],
            'image/jpeg', sys.getsizeof(output), None)
        return product_pic
示例#26
0
class RAAppointment(models.Model):
    """
    This stores information about a (Research Assistant)s application and pay.
    """
    person = models.ForeignKey(Person, help_text='The RA who is being appointed.', null=False, blank=False, related_name='ra_person', on_delete=models.PROTECT)
    sin = models.PositiveIntegerField(null=True, blank=True)
    # We want do add some sort of accountability for checking visas.
    visa_verified = models.BooleanField(default=False, help_text='I have verified this RA\'s visa information')
    hiring_faculty = models.ForeignKey(Person, help_text='The manager who is hiring the RA.', related_name='ra_hiring_faculty', on_delete=models.PROTECT)
    unit = models.ForeignKey(Unit, help_text='The unit that owns the appointment', null=False, blank=False, on_delete=models.PROTECT)
    hiring_category = models.CharField(max_length=4, choices=HIRING_CATEGORY_CHOICES, default='GRA')
    scholarship = models.ForeignKey(Scholarship, null=True, blank=True, help_text='Scholarship associated with this appointment. Optional.', on_delete=models.PROTECT)
    project = models.ForeignKey(Project, null=False, blank=False, on_delete=models.PROTECT)
    account = models.ForeignKey(Account, null=False, blank=False, help_text='This is now called "Object" in the new PAF', on_delete=models.PROTECT)
    program = models.ForeignKey(Program, null=True, blank=True, help_text='If none is provided,  "00000" will be added in the PAF', on_delete=models.PROTECT)
    start_date = models.DateField(auto_now=False, auto_now_add=False)
    end_date = models.DateField(auto_now=False, auto_now_add=False)
    pay_frequency = models.CharField(max_length=60, choices=PAY_FREQUENCY_CHOICES, default='B')
    lump_sum_pay = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Total Pay")
    lump_sum_hours = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Total Hours", blank=True, null=True)
    biweekly_pay = models.DecimalField(max_digits=8, decimal_places=2)
    pay_periods = models.DecimalField(max_digits=6, decimal_places=1)
    hourly_pay = models.DecimalField(max_digits=8, decimal_places=2)
    hours = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Biweekly Hours")
    reappointment = models.BooleanField(default=False, help_text="Are we re-appointing to the same position?")
    medical_benefits = models.BooleanField(default=False, help_text="50% of Medical Service Plan")
    dental_benefits = models.BooleanField(default=False, help_text="50% of Dental Plan")
    #  The two following fields verbose names are reversed for a reason.  They were named incorrectly with regards to
    #  the PAF we generate, so the verbose names are correct.
    notes = models.TextField("Comments", blank=True, help_text="Biweekly employment earnings rates must include vacation pay, hourly rates will automatically have vacation pay added. The employer cost of statutory benefits will be charged to the amount to the earnings rate.")
    comments = models.TextField("Notes", blank=True, help_text="For internal use")
    offer_letter_text = models.TextField(null=True, help_text="Text of the offer letter to be signed by the RA and supervisor.")

    def autoslug(self):
        if self.person.userid:
            ident = self.person.userid
        else:
            ident = str(self.person.emplid)
        return make_slug(self.unit.label + '-' + str(self.start_date.year) + '-' + ident)
    slug = AutoSlugField(populate_from='autoslug', null=False, editable=False, unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    deleted = models.BooleanField(null=False, default=False)
    config = JSONField(null=False, blank=False, default=dict) # addition configuration stuff
    defaults = {'use_hourly': False}
    use_hourly, set_use_hourly = getter_setter('use_hourly')

    def __str__(self):
        return str(self.person) + "@" + str(self.created_at)

    class Meta:
        ordering = ['person', 'created_at']

    def save(self, *args, **kwargs):
        # set SIN field on the Person object
        if self.sin and 'sin' not in self.person.config:
            self.person.set_sin(self.sin)
            self.person.save()
        super(RAAppointment, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('ra:view', kwargs={'ra_slug': self.slug})

    def mark_reminded(self):
        self.config['reminded'] = True
        self.save()

    @staticmethod
    def letter_choices(units):
        """
        Return a form choices list for RA letter templates in these units.

        Ignores the units for now: we want to allow configurability later.
        """
        return [(key, label) for (key, (label, _, _)) in list(DEFAULT_LETTERS.items())]

    def build_letter_text(self, selection):
        """
        This takes the value passed from the letter selector menu and builds the appropriate
        default letter based on that.
        """
        substitutions = {
            'start_date': self.start_date.strftime("%B %d, %Y"),
            'end_date': self.end_date.strftime("%B %d, %Y"),
            'lump_sum_pay': self.lump_sum_pay,
            'biweekly_pay': self.biweekly_pay,
            }

        _, lumpsum_text, biweekly_text = DEFAULT_LETTERS[selection]

        if self.pay_frequency == 'B':
            text = biweekly_text
        else:
            text = lumpsum_text

        letter_text = text % substitutions
        self.offer_letter_text = letter_text
        self.save()

    def letter_paragraphs(self):
        """
        Return list of paragraphs in the letter (for PDF creation)
        """
        text = self.offer_letter_text
        text = normalize_newlines(text)
        return text.split("\n\n") 
    
    @classmethod
    def semester_guess(cls, date):
        """
        Guess the semester for a date, in the way that financial people do (without regard to class start/end dates)
        """
        mo = date.month
        if mo <= 4:
            se = 1
        elif mo <= 8:
            se = 4
        else:
            se = 7
        semname = str((date.year-1900)*10 + se)
        return Semester.objects.get(name=semname)

    @classmethod
    def start_end_dates(cls, semester):
        """
        First and last days of the semester, in the way that financial people do (without regard to class start/end dates)
        """
        return Semester.start_end_dates(semester)
        #yr = int(semester.name[0:3]) + 1900
        #sm = int(semester.name[3])
        #if sm == 1:
        #    start = datetime.date(yr, 1, 1)
        #    end = datetime.date(yr, 4, 30)
        #elif sm == 4:
        #    start = datetime.date(yr, 5, 1)
        #    end = datetime.date(yr, 8, 31)
        #elif sm == 7:
        #    start = datetime.date(yr, 9, 1)
        #    end = datetime.date(yr, 12, 31)
        #return start, end
        
    def start_semester(self):
        "Guess the starting semester of this appointment"
        start_semester = RAAppointment.semester_guess(self.start_date)
        # We do this to eliminate hang - if you're starting N days before 
        # semester 1134, you aren't splitting that payment across 2 semesters. 
        start, end = RAAppointment.start_end_dates(start_semester)
        if end - self.start_date < datetime.timedelta(SEMESTER_SLIDE):
            return start_semester.next_semester()
        return start_semester

    def end_semester(self):
        "Guess the ending semester of this appointment"
        end_semester = RAAppointment.semester_guess(self.end_date)
        # We do this to eliminate hang - if you're starting N days after 
        # semester 1134, you aren't splitting that payment across 2 semesters. 
        start, end = RAAppointment.start_end_dates(end_semester)
        if self.end_date - start < datetime.timedelta(SEMESTER_SLIDE):
            return end_semester.previous_semester()
        return end_semester

    def semester_length(self):
        "The number of semesters this contracts lasts for"
        return self.end_semester() - self.start_semester() + 1

    @classmethod
    def expiring_appointments(cls):
        """
        Get the list of RA Appointments that will expire in the next few weeks so we can send a reminder email
        """
        today = datetime.datetime.now()
        min_age = datetime.datetime.now() + datetime.timedelta(days=14)
        expiring_ras = RAAppointment.objects.filter(end_date__gt=today, end_date__lte=min_age, deleted=False)
        ras = [ra for ra in expiring_ras if 'reminded' not in ra.config or not ra.config['reminded']]
        return ras

    @classmethod
    def email_expiring_ras(cls):
        """
        Emails the supervisors of the RAs who have appointments that are about to expire.
        """
        subject = 'RA appointment expiry reminder'
        from_email = settings.DEFAULT_FROM_EMAIL

        expiring_ras = cls.expiring_appointments()
        template = get_template('ra/emails/reminder.txt')

        for raappt in expiring_ras:
            supervisor = raappt.hiring_faculty
            context = {'supervisor': supervisor, 'raappt': raappt}
            # Let's see if we have any Funding CC supervisors that should also get the reminder.
            cc = None
            fund_cc_roles = Role.objects_fresh.filter(unit=raappt.unit, role='FDCC')
            # If we do, let's add them to the CC list, but let's also make sure to use their role account email for
            # the given role type if it exists.
            if fund_cc_roles:
                people = []
                for role in fund_cc_roles:
                    people.append(role.person)
                people = list(set(people))
                cc = []
                for person in people:
                    cc.append(person.role_account_email('FDCC'))
            msg = EmailMultiAlternatives(subject, template.render(context), from_email, [supervisor.email()],
                                         headers={'X-coursys-topic': 'ra'}, cc=cc)
            msg.send()
            raappt.mark_reminded()

    def get_program_display(self):
        if self.program:
            return self.program.get_program_number_display()
        else:
            return '00000'

    def has_attachments(self):
        return self.attachments.visible().count() > 0
示例#27
0
class FormWizardEntry(models.Model):
    """Form wizard entry."""

    user = models.ForeignKey(AUTH_USER_MODEL,
                             verbose_name=_("User"),
                             on_delete=models.CASCADE)
    name = models.CharField(_("Name"), max_length=255)
    title = models.CharField(_("Title"),
                             max_length=255,
                             null=True,
                             blank=True,
                             help_text=_("Shown in templates if available."))
    slug = AutoSlugField(populate_from='name',
                         verbose_name=_("Slug"),
                         unique=True)
    is_public = models.BooleanField(
        _("Is public?"),
        default=False,
        help_text=_("Makes your form wizard visible to the public."))
    is_cloneable = models.BooleanField(
        _("Is cloneable?"),
        default=False,
        help_text=_("Makes your form wizard cloneable by other users."))
    success_page_title = models.CharField(
        _("Success page title"),
        max_length=255,
        null=True,
        blank=True,
        help_text=_("Custom message title to display after valid form is "
                    "submitted"))
    success_page_message = models.TextField(
        _("Success page body"),
        null=True,
        blank=True,
        help_text=_("Custom message text to display after valid form is "
                    "submitted"))
    show_all_navigation_buttons = models.BooleanField(
        _("Show all navigation buttons?"),
        default=False,
        help_text=_("Show all navigation buttons."))
    # action = models.CharField(
    #     _("Action"), max_length=255, null=True, blank=True,
    #     help_text=_("Custom form action; don't fill this field, unless "
    #                 "really necessary.")
    # )
    wizard_type = models.CharField(_("Type"),
                                   max_length=255,
                                   null=False,
                                   blank=False,
                                   choices=WIZARD_TYPES,
                                   default=DEFAULT_WIZARD_TYPE,
                                   help_text=_("Type of the form wizard."))
    created = models.DateTimeField(_("Created"),
                                   null=True,
                                   blank=True,
                                   auto_now_add=True)
    updated = models.DateTimeField(_("Updated"),
                                   null=True,
                                   blank=True,
                                   auto_now=True)

    class Meta(object):
        """Meta class."""

        verbose_name = _("Form wizard entry")
        verbose_name_plural = _("Form wizard entries")
        unique_together = (
            ('user', 'slug'),
            ('user', 'name'),
        )

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        """Get absolute URL.

        Absolute URL, which goes to the form-wizard view view.

        :return string:
        """
        return reverse('fobi.view_form_wizard_entry',
                       kwargs={'form_wizard_entry_slug': self.slug})
示例#28
0
class ProductModelBase(CreateUpdateModelBase):
    '''
    This is the base class that all Products should inherit from.
    '''
    sku = models.CharField(
        _("SKU"),
        max_length=40,
        unique=True,
        blank=True,
        null=True,
        help_text=_("User Defineable SKU field")
    )  # Needs to be autogenerated by default, and unique from the PK
    uuid = models.UUIDField(default=uuid.uuid4, editable=False,
                            unique=True)  # Used to track the product
    name = models.CharField(_("Name"), max_length=80, blank=False)
    site = models.ForeignKey(Site,
                             verbose_name=_("Site"),
                             on_delete=models.CASCADE,
                             default=settings.SITE_ID,
                             related_name="products")  # For multi-site support
    slug = AutoSlugField(populate_from='name',
                         unique_with='site__id')  # Gets set in the save
    available = models.BooleanField(
        _("Available"),
        default=False,
        help_text=_("Is this currently available?")
    )  # This can be forced to be unavailable if there is no prices attached.
    description = models.JSONField(
        _("Description"),
        default=product_description_default,
        blank=True,
        null=True,
        help_text=_("Eg: {'call out': 'The ultimate product'}"))
    meta = models.JSONField(
        _("Meta"),
        validators=[validate_msrp],
        default=product_meta_default,
        blank=True,
        null=True,
        help_text=_(
            "Eg: { 'msrp':{'usd':10.99} }\n(iso4217 Country Code):(MSRP Price)"
        ))
    classification = models.ManyToManyField(
        "vendor.TaxClassifier",
        blank=True)  # What taxes can apply to this item
    offers = models.ManyToManyField("vendor.Offer",
                                    blank=True,
                                    related_name="products")
    receipts = models.ManyToManyField("vendor.Receipt",
                                      blank=True,
                                      related_name="products")

    objects = models.Manager()
    on_site = CurrentSiteManager()

    class Meta:
        abstract = True

    def __str__(self):
        return self.name

    def get_msrp(self, currency):
        if currency in self.meta['msrp']:
            return self.meta['msrp'][currency]
        else:
            return self.meta['msrp'][self.meta['msrp']['default']]

    def add_to_cart_url(self):
        """
        Link to add the item to the user's cart.
        """

    # TODO: ADD trigger when object becomes unavailable to disable offer if it exisits.

    def get_best_currency(self, currency=DEFAULT_CURRENCY):
        """
        If no currency is provided as an argument it will default to the products's msrp default currency.
        If currency is provided but is not available in the product it will default to the products's msrp default currency.
        """
        if is_currency_available(self.meta['msrp'].keys(), currency=currency):
            return currency
        else:
            return self.meta['msrp']['default']
示例#29
0
class DataRequestProject(models.Model):
    """
    Base class for data request projects.

    Some fields are only available to Open Humans admins, including:
        all_sources_access (Boolean): when True, all data sources shared w/proj
        approved (Boolean): when True, member cap is removed and proj is listed
        token_expiration_disabled (Boolean): if True master tokens don't expire
    """

    BOOL_CHOICES = ((True, "Yes"), (False, "No"))
    STUDY_CHOICES = ((True, "Study"), (False, "Activity"))

    is_study = models.BooleanField(
        choices=STUDY_CHOICES,
        help_text=('A "study" is doing human subjects research and must have '
                   "Institutional Review Board approval or equivalent ethics "
                   "board oversight. Activities can be anything else, e.g. "
                   "data visualizations."),
        verbose_name="Is this project a study or an activity?",
    )
    name = models.CharField(max_length=100, verbose_name="Project name")
    slug = AutoSlugField(populate_from="name", unique=True, always_update=True)
    leader = models.CharField(
        max_length=100, verbose_name="Leader(s) or principal investigator(s)")
    organization = models.CharField(blank=True,
                                    max_length=100,
                                    verbose_name="Organization or institution")
    is_academic_or_nonprofit = models.BooleanField(
        choices=BOOL_CHOICES,
        verbose_name=("Is this institution or organization an academic "
                      "institution or non-profit organization?"),
    )
    add_data = models.BooleanField(
        help_text=('If your project collects data, choose "Add data" here. If '
                   'you choose "Add data", you will need to provide a '
                   '"Returned data description" below.'),
        verbose_name="Add data",
        default=False,
    )
    explore_share = models.BooleanField(
        help_text=("If your project performs analysis on data, choose "
                   '"Explore & share".'),
        verbose_name="Explore & share",
        default=False,
    )
    contact_email = models.EmailField(
        verbose_name="Contact email for your project")
    info_url = models.URLField(
        blank=True,
        verbose_name="URL for general information about your project")
    short_description = models.CharField(
        max_length=140,
        verbose_name="A short description (140 characters max)")
    long_description = models.TextField(
        max_length=1000,
        verbose_name="A long description (1000 characters max)")
    returned_data_description = models.CharField(
        blank=True,
        max_length=140,
        verbose_name=("Description of data you plan to upload to member "
                      " accounts (140 characters max)"),
        help_text=("Leave this blank if your project doesn't plan to add or "
                   "return new data for your members.  If your project is set "
                   'to be displayed under "Add data", then you must provide '
                   "this information."),
    )
    active = models.BooleanField(choices=BOOL_CHOICES,
                                 help_text=active_help_text,
                                 default=True)
    badge_image = models.ImageField(
        blank=True,
        storage=PublicStorage(),
        upload_to=badge_upload_path,
        max_length=1024,
        help_text=("A badge that will be displayed on the user's profile once "
                   "they've connected your project."),
    )
    requested_sources = models.ManyToManyField(
        "self",
        related_name="requesting_projects",
        symmetrical=False,
        blank=True)
    all_sources_access = models.BooleanField(default=False)
    deauth_email_notification = models.BooleanField(
        default=False,
        help_text="Receive emails when a member deauthorizes your project",
        verbose_name="Deauthorize email notifications",
    )
    erasure_supported = models.BooleanField(
        default=False,
        help_text=
        "Whether your project supports erasing a member's data on request",
    )
    request_username_access = models.BooleanField(
        choices=BOOL_CHOICES,
        help_text=("Access to the member's username. This implicitly enables "
                   "access to anything the user is publicly sharing on Open "
                   "Humans. Note that this is potentially sensitive and/or "
                   "identifying."),
        verbose_name="Are you requesting Open Humans usernames?",
    )
    registered_datatypes = models.ManyToManyField(DataType, blank=True)

    class Meta:
        ordering = ["name"]

    coordinator = models.ForeignKey(Member, on_delete=models.PROTECT)
    approved = models.BooleanField(default=False)
    approval_history = ArrayField(
        ArrayField(models.CharField(max_length=32), size=2),
        default=list,
        editable=False,
    )
    created = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)

    master_access_token = models.CharField(max_length=64, default=generate_id)

    token_expiration_date = models.DateTimeField(default=now_plus_24_hours)
    token_expiration_disabled = models.BooleanField(default=False)
    no_public_data = models.BooleanField(default=False)
    auto_add_datatypes = models.BooleanField(default=False)

    def __init__(self, *args, **kwargs):
        # Adds self.old_approved so that we can detect when the field changes
        super().__init__(*args, **kwargs)
        self.old_approved = self.approved

    def __str__(self):
        return str("{0}").format(self.name)

    def save(self, *args, **kwargs):
        """
        Override save to update the timestamp for when approved gets changed.
        """
        if self.old_approved != self.approved:
            self.approval_history.append(
                (self.approved, datetime.datetime.utcnow().isoformat()))
        return super().save(*args, **kwargs)

    @property
    def project_approval_date(self):
        """
        Returns None if project is not approved, most recent approval date
        otherwise.
        """
        if not self.approved:
            return None
        if self.approval_history == []:
            return None
        return dateutil.parser.parse(self.approval_history[-1][1])

    def refresh_token(self):
        """
        Generate a new master access token that expires in 24 hours.
        """
        self.master_access_token = generate_id()
        self.token_expiration_date = now_plus_24_hours()

        self.save()

    @property
    def id_label(self):
        return str("direct-sharing-{0}").format(self.id)

    @property
    def project_type(self):
        return "study" if self.is_study else "activity"

    @property
    def type(self):
        if hasattr(self, "oauth2datarequestproject"):
            return "oauth2"

        if hasattr(self, "onsitedatarequestproject"):
            return "on-site"

    @property
    def authorized_members(self):
        return self.project_members.filter_active().count()

    def active_user(self, user):
        try:
            return DataRequestProjectMember.objects.get(
                member__user=user,
                project=self,
                joined=True,
                authorized=True,
                revoked=False,
            )
        except (TypeError, AttributeError,
                DataRequestProjectMember.DoesNotExist):
            return None

    def is_joined(self, user):
        if self.active_user(user):
            return True
        else:
            return False

    @property
    def join_url(self):
        if self.type == "on-site":
            return reverse("direct-sharing:join-on-site",
                           kwargs={"slug": self.slug})
        return self.oauth2datarequestproject.enrollment_url

    @property
    def connect_verb(self):
        return "join" if self.type == "on-site" else "connect"

    def delete_without_cascade(self, using=None, keep_parents=False):
        """
        Modified version of django's default delete() method.

        This method is added to enable safe deletion of the child models without
        removing objects related to it through the parent. As of Feb 2017,
        no models are directly related to the OAuth2DataRequestProject or
        OnSiteDataRequestProject child models.
        """
        allowed_models = [
            "private_sharing.onsitedatarequestproject",
            "private_sharing.oauth2datarequestproject",
        ]
        if self._meta.label_lower not in allowed_models:
            raise Exception("'delete_without_cascade' only for child models!")
        using = using or router.db_for_write(self.__class__, instance=self)
        assert self._get_pk_val() is not None, (
            "%s object can't be deleted because its %s attribute is set to None."
            % (self._meta.object_name, self._meta.pk.attname))

        collector = Collector(using=using)
        collector.collect([self],
                          keep_parents=keep_parents,
                          collect_related=False)
        return collector.delete()
示例#30
0
文件: models.py 项目: Zevgon/SRS
class Language(models.Model):
    title = models.CharField(max_length=100)
    title_slug = AutoSlugField(unique=True, populate_from='title')

    def __str__(self):
        return self.title