Beispiel #1
0
class Migration(migrations.Migration):

    dependencies = [
        ('events', '0003_split_dates'),
    ]

    operations = [
        migrations.RenameField(
            model_name='event',
            old_name='what',
            new_name='description',
        ),
        migrations.RenameField(
            model_name='event',
            old_name='what_rendered',
            new_name='description_rendered',
        ),
        migrations.RenameField(
            model_name='event',
            old_name='where',
            new_name='location',
        ),
        migrations.AlterField(
            model_name='event',
            name='description',
            field=MarkdownField(rendered_field='description_rendered'),
        ),
    ]
Beispiel #2
0
class NewsStory(models.Model):

    headline = models.CharField(max_length=512)
    short_story = models.TextField()

    text = MarkdownField(rendered_field="text_rendered",
                         validator=VALIDATOR_STANDARD)
    text_rendered = RenderedMarkdownField()

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)
    published = models.BooleanField(default=False)
    url = models.URLField(blank=True, null=True)

    @property
    def url_read_more(self):
        if self.url:
            return self.url
        return reverse("news:story", kwargs={"pk": self.pk})

    def __str__(self):
        return self.headline

    class Meta:
        ordering = ("-created", )
        verbose_name = _("News story")
        verbose_name_plural = _("News stories")
Beispiel #3
0
class Page(models.Model):
    title = models.CharField(max_length=100, help_text="100 characters or fewer.")
    subtitle = models.CharField(
        max_length=100, blank=True, default='', help_text="100 characters or fewer. Optional."
    )
    modified = models.DateTimeField(auto_now=True, verbose_name="date modified")

    slug = models.SlugField(
        unique=True,
        help_text="Changing this value after initial creation will break existing "
                  "page URLs. Must be unique.",
    )

    body = MarkdownField(rendered_field='body_rendered', validator=VALIDATOR_CLASSY)
    body_rendered = RenderedMarkdownField()

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('content:page', kwargs={'slug': self.slug})

    class Meta:
        verbose_name = "page"
        verbose_name_plural = "pages"
Beispiel #4
0
class Post(PublishedModel):
    title = models.CharField(max_length=100, help_text="100 characters or fewer.")
    slug = models.SlugField(unique=True, help_text="Changing this value after initial creation will break existing "
                                                   "post URLs. Must be unique.")
    tags = TaggableManager(blank=True)

    allow_comments = models.BooleanField(default=True)

    author = models.CharField(max_length=50, help_text="50 characters or fewer.")
    created = models.DateTimeField(auto_now_add=True, verbose_name="date created")
    modified = models.DateTimeField(auto_now=True, verbose_name="date modified")

    image = VersatileImageField(upload_to='blog', blank=True, null=True, ppoi_field='image_ppoi')
    image_ppoi = PPOIField()

    description = models.TextField(max_length=140, blank=True, help_text="140 characters or fewer.")

    text = MarkdownField(rendered_field='text_rendered', validator=VALIDATOR_CLASSY)
    text_rendered = RenderedMarkdownField()

    class Meta:
        ordering = ['-created']

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('blog:detail', kwargs={'slug': self.slug})

    @cached_property
    def structured_data(self):
        url = settings.SITE_URL + self.get_absolute_url()
        data = {
            '@type': 'BlogPosting',
            'headline': self.title,
            'description': self.description or Truncator(strip_tags(self.text_rendered)).chars(200),
            'author': {
                '@type': 'Person',
                'name': self.author
            },
            'datePublished': self.created.strftime('%Y-%m-%d'),
            'dateModified': self.modified.strftime('%Y-%m-%d'),
            'publisher': None,
            'url': url,
            'mainEntityOfPage': {
                '@type': 'WebPage',
                '@id': url
            },
        }
        if self.image:
            data['image'] = {
                '@type': 'ImageObject',
                'url': settings.SITE_URL + self.image.url,
                'height': self.image.height,
                'width': self.image.width
            }

        return data
Beispiel #5
0
class PrimaryEffect(models.Model):
    """
    The Primary Effect model holds data for effects of Project Activities that are classified as Primary


    """

    # IDENTIFICATION

    primary_effect_identifier = models.CharField(
        max_length=80,
        blank=True,
        null=True,
        help_text='A unique identification of a Primary Effect internal use')

    primary_effect_description = MarkdownField(
        blank=True,
        null=True,
        rendered_field='text_rendered',
        validator=VALIDATOR_STANDARD,
        help_text=
        'Textual description of a Primary Effect. Markdown format is supported <a class="risk_manual_url" href="https://www.openriskmanual.org/wiki/Primary_GHG_Effects">Documentation</a>'
    )

    # text = MarkdownField(rendered_field='text_rendered', validator=VALIDATOR_STANDARD)
    text_rendered = RenderedMarkdownField(blank=True, null=True)

    # LINKS
    project_activity = models.ForeignKey(
        'ProjectActivity',
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        help_text="The Project Activity to which this Primary Effect belongs")

    effect_category = models.IntegerField(
        blank=True,
        null=True,
        choices=PRIMARY_GHG_EFFECTS,
        help_text=
        'The general category to which the Primary Effect belongs <a class="risk_manual_url" href="https://www.openriskmanual.org/wiki/Primary_GHG_Effects">Documentation</a>'
    )

    #
    # BOOKKEEPING FIELDS
    #
    creation_date = models.DateTimeField(auto_now_add=True)
    last_change_date = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.primary_effect_identifier

    def get_absolute_url(self):
        return reverse('portfolio:PrimaryEffect_edit', kwargs={'pk': self.pk})

    class Meta:
        verbose_name = "Primary Effect"
        verbose_name_plural = "Primary Effects"
Beispiel #6
0
class Location(models.Model):
    slug = AutoSlugField(
        always_update=True,
        # slugify=lambda v: ''.join(
        #     [l for l in v
        #         .replace('æ','ae').replace('ø','oe').replace('å','aa')
        #         .lower() if l.isalpha() or l == " "
        #     ]).replace(" ", '-'),
        populate_from='name',
        unique_with=('municipality')  #, 'county')
    )

    ssr_id = models.PositiveIntegerField(blank=True,
                                         null=True,
                                         unique=True,
                                         verbose_name="SSR ID")
    name_types = models.ManyToManyField(NameType)

    name = models.CharField(max_length=150)
    municipality = models.ForeignKey(Municipality,
                                     null=True,
                                     on_delete=models.SET_NULL)
    place = models.CharField(max_length=150, null=True, blank=True)
    # description = models.TextField(max_length=1000, null=True, blank=True)
    description_md = MarkdownField(
        max_length=1500,
        null=True,
        blank=True,
        use_editor=False,
        use_admin_editor=False,
        rendered_field='description',
        # validator=MARKDOWN_VALIDATOR,
    )
    description = RenderedMarkdownField(null=True, blank=True)

    geometry = models.PolygonField()

    created_at = models.DateTimeField(auto_now_add=True)

    @property
    def county(self):
        return self.municipality.get_county_display()

    @property
    def full_address(self):
        if self.place:
            return "{}, {}, {}".format(self.name, self.place,
                                       self.municipality.name)
        return "{}, {}".format(self.name, self.municipality.name)

    class Meta:
        ordering = ('name', )

    def __str__(self):
        return f"{self.full_address}"  # [{self.name_type.name}]"
Beispiel #7
0
class Project(models.Model):
    category = models.ForeignKey(Category,
                                 related_name='projects',
                                 on_delete=models.CASCADE)
    user = models.ForeignKey(User,
                             related_name='projects',
                             on_delete=models.CASCADE)
    name = models.CharField(max_length=200, db_index=True)
    slug = models.SlugField(max_length=200, db_index=True)
    tags = TaggableManager()
    description = MarkdownField(rendered_field='description_rendered',
                                validator=VALIDATOR_STANDARD,
                                blank=True)
    description_rendered = RenderedMarkdownField()
    video = models.URLField(blank=True)
    goal = models.DecimalField(max_digits=10, decimal_places=2)
    collected_sum = models.DecimalField(max_digits=10,
                                        decimal_places=2,
                                        default=0)
    expiration_date = models.DateField()
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    backers = models.PositiveIntegerField(default=0)
    rate = models.FloatField(
        validators=[MinValueValidator(0),
                    MaxValueValidator(5)], default=0)

    class Meta:
        ordering = ('name', )
        index_together = (('id', 'slug'), )

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('project', args=[self.slug, self.id])

    def save(self, *args, **kwargs):
        self.slug = slugify(self.name)
        self.video = self.video.replace('youtu.be',
                                        'www.youtube.com/embed').replace(
                                            'watch?v=', 'embed/')
        super(Project, self).save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        super(Project, self).delete(*args, **kwargs)
        Tag.objects.annotate(ntag=Count('taggit_taggeditem_items')).filter(
            ntag=0).delete()

    def days_left(self):
        return (self.expiration_date - date.today()).days
Beispiel #8
0
class Production(models.Model):
    type = models.CharField(max_length=4, choices=PRODUCTION_TYPES)
    slug = AutoSlugField(always_update=True,
                         populate_from='title',
                         unique_with=('type', ))
    title = models.CharField(max_length=50)
    release_date = models.DateField('Released')
    poster = ImageField(upload_to=PathAndRename('posters/'),
                        blank=True,
                        null=True)

    summary_md = MarkdownField(max_length=1000,
                               null=True,
                               blank=True,
                               rendered_field='summary')
    summary = RenderedMarkdownField(null=True, blank=True)
    runtime = models.PositiveIntegerField(null=True, blank=True)

    tmdb_id = models.CharField(max_length=10,
                               null=True,
                               blank=True,
                               unique=True,
                               verbose_name="TMDb ID")
    imdb_id = models.CharField(max_length=10,
                               null=True,
                               blank=True,
                               unique=True,
                               verbose_name="IMDb ID")
    nbdb_id = models.CharField(max_length=10,
                               null=True,
                               blank=True,
                               unique=True,
                               verbose_name="NBDb ID")

    crew = models.ManyToManyField(Person, through='people.Job')
    locations = models.ManyToManyField('locations.Location',
                                       through='productions.Scene')
    created_at = models.DateTimeField(auto_now_add=True)

    @property
    def year(self):
        return self.release_date.year

    class Meta:
        ordering = ('title', )

    def __str__(self):
        return f"{self.title} ({self.year})"
Beispiel #9
0
class Migration(migrations.Migration):

    initial = True

    dependencies = [
        ('taggit', '0003_taggeditem_add_unique_index'),
    ]

    operations = [
        migrations.CreateModel(
            name='Event',
            fields=[
                ('id',
                 models.AutoField(auto_created=True,
                                  primary_key=True,
                                  serialize=False,
                                  verbose_name='ID')),
                ('title',
                 models.CharField(help_text='100 characters or fewer.',
                                  max_length=100)),
                ('where', models.CharField(max_length=200)),
                ('what', MarkdownField(rendered_field='what_rendered')),
                ('what_rendered', RenderedMarkdownField()),
                ('start', models.DateTimeField()),
                ('end', models.DateTimeField()),
                ('created',
                 models.DateTimeField(auto_now_add=True,
                                      verbose_name='date created')),
                ('modified',
                 models.DateTimeField(auto_now=True,
                                      verbose_name='date modified')),
                ('image',
                 versatileimagefield.fields.VersatileImageField(
                     blank=True, null=True, upload_to='events')),
                ('image_ppoi',
                 versatileimagefield.fields.PPOIField(default='0.5x0.5',
                                                      editable=False,
                                                      max_length=20)),
                ('tags',
                 taggit.managers.TaggableManager(
                     blank=True,
                     help_text='A comma-separated list of tags.',
                     through='taggit.TaggedItem',
                     to='taggit.Tag',
                     verbose_name='Tags')),
            ],
        ),
    ]
Beispiel #10
0
class BlogPost(models.Model):
    """the post data"""
    user_profile = models.ForeignKey(settings.AUTH_USER_MODEL,
                                     on_delete=models.CASCADE)
    title = models.CharField(max_length=100)
    autor = models.CharField(max_length=35)
    is_featured = models.BooleanField(default=False)
    date_published = models.DateTimeField(default=timezone.now)
    # body = models.CharField(max_length=6000)
    body = MarkdownField(rendered_field='text_rendered',
                         validator=VALIDATOR_STANDARD)
    image_link = models.CharField(max_length=200)
    description = models.CharField(max_length=1000)
    text_rendered = RenderedMarkdownField()

    def __str__(self):
        """return the post title as string"""
        return self.title
Beispiel #11
0
class Migration(migrations.Migration):

    dependencies = [
        ('foxtail_blog', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='post',
            name='text_rendered',
            field=models.TextField(blank=True),
        ),
        migrations.AlterField(
            model_name='post',
            name='text',
            field=MarkdownField(),
        ),
    ]
class Migration(migrations.Migration):

    dependencies = [
        ('foxtail_blog', '0005_comment_length'),
    ]

    operations = [
        migrations.AlterField(
            model_name='post',
            name='text_rendered',
            field=RenderedMarkdownField(),
        ),
        migrations.AlterField(
            model_name='post',
            name='text',
            field=MarkdownField(rendered_field='text_rendered'),
        ),
    ]
Beispiel #13
0
class News(models.Model):
    project = models.ForeignKey(Project,
                                on_delete=models.CASCADE,
                                related_name='news')
    title = models.CharField(max_length=200, db_index=True)
    text = MarkdownField(rendered_field='text_rendered',
                         validator=VALIDATOR_STANDARD,
                         blank=True)
    text_rendered = RenderedMarkdownField()
    image = CloudinaryField('image', null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('-created', )
        verbose_name = 'news'
        verbose_name_plural = 'news'

    def get_absolute_url(self):
        return reverse('news', args=[self.id])
Beispiel #14
0
class Scene(models.Model):
    production = models.ForeignKey(Production, on_delete=models.CASCADE)
    description_md = MarkdownField(max_length=800,
                                   null=True,
                                   blank=True,
                                   rendered_field='description')
    description = RenderedMarkdownField(null=True, blank=True)
    location = models.ForeignKey('locations.Location',
                                 blank=True,
                                 null=True,
                                 on_delete=models.SET_NULL)

    @property
    def is_unidentified(self):
        return True if not self.location else False

    def __str__(self):
        return '{}: {}'.format(
            self.production.title,
            self.location.full_address if self.location else '(None)')
Beispiel #15
0
class Research(models.Model):
    scenes = models.ManyToManyField(Scene,
                                    limit_choices_to={'location': None},
                                    related_name='research')
    best_guess = models.CharField(max_length=50)
    description_md = MarkdownField(
        max_length=1500,
        null=True,
        blank=True,
        use_editor=False,
        use_admin_editor=False,
        rendered_field='description',
    )
    description = RenderedMarkdownField(null=True, blank=True)
    geometry = models.PointField(blank=True, null=True)

    @property
    def scene_count(self):
        return self.scenes.count()

    class Meta:
        verbose_name_plural = 'research'
Beispiel #16
0
class Batch(BuskerModel):
    """
    Represents a batch of Download codes generated for a given DownloadableWork object.
    """
    label = models.CharField(max_length=255)
    work = models.ForeignKey(DownloadableWork, on_delete=models.CASCADE)
    private_note = models.TextField(
        null=True,
        blank=True,
        help_text="Optional note for private use; will not be "
        "displayed to end users.")
    public_message = MarkdownField(
        null=True,
        blank=True,
        validator=VALIDATOR_STANDARD,
        rendered_field='public_message_rendered',
        help_text=
        "Optional markdown-formatted message that will be displayed on the "
        "download page.")
    public_message_rendered = RenderedMarkdownField()
    number_of_codes = models.IntegerField(
        help_text=
        "The number of Download Codes to be generated with this batch. "
        "(Additional codes may be added to the batch later.)",
        default=100)  # TODO place an upper limit?
    max_uses = models.IntegerField(
        help_text=
        "The number of times this code can be used. (You may subsequently change "
        "this amount on individual codes, but this will be used as the initial "
        "value.) "
        "(0 = unlimited)",
        default=3)

    def __str__(self):
        return f"{self.label} -- {self.work.title} by {self.work.artist.name}"

    class Meta:
        verbose_name_plural = "Batches"
        ordering = ('-created_date', 'work__artist__name', 'work__title')
Beispiel #17
0
class ActivityBarrier(models.Model):
    """
    The Activity Barrier model holds data for barriers to Project Activities


    """

    # IDENTIFICATION

    barrier_identifier = models.CharField(max_length=80, blank=True, null=True, help_text='A unique identification of a Barrier')

    barrier_description = MarkdownField(blank=True, null=True, rendered_field='text_rendered',
                                                 validator=VALIDATOR_STANDARD,
                                                 help_text='Textual description of an Activity Barrier. Markdown format is supported <a class="risk_manual_url" href="https://www.openriskmanual.org/wiki/">Documentation</a>')

    text_rendered = RenderedMarkdownField()

    # LINKS
    project_activity = models.ForeignKey(ProjectActivity, blank=True, null=True, on_delete=models.CASCADE, help_text="The Project Activity to which this Primary Effect belongs")


    #
    # BOOKKEEPING FIELDS
    #
    creation_date = models.DateTimeField(auto_now_add=True)
    last_change_date = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.barrier_identifier

    def get_absolute_url(self):
        return reverse('risk:ActivityBarrier_edit', kwargs={'pk': self.pk})

    class Meta:
        verbose_name = "Activity Barrier"
        verbose_name_plural = "Activity Barrier"
Beispiel #18
0
class Event(models.Model):
    title = models.CharField(max_length=100,
                             help_text="100 characters or fewer.")
    slug = AutoSlugField(populate_from='title', unique_for_year='start')
    tags = TaggableManager(blank=True)

    description = MarkdownField(rendered_field='description_rendered',
                                validator=VALIDATOR_CLASSY)
    description_rendered = RenderedMarkdownField()
    url = models.URLField(blank=True)

    location = models.CharField(max_length=200)
    address = models.TextField(blank=True)

    start = models.DateField()
    start_time = models.TimeField(null=True,
                                  blank=True,
                                  help_text="Time is optional.")
    end = models.DateField(null=True,
                           blank=True,
                           help_text="End date and time are optional.")
    end_time = models.TimeField(null=True,
                                blank=True,
                                help_text="End date and time are optional.")

    image = VersatileImageField(upload_to='events',
                                blank=True,
                                null=True,
                                ppoi_field='image_ppoi')
    image_ppoi = PPOIField()

    created = models.DateTimeField(auto_now_add=True,
                                   verbose_name="date created")
    modified = models.DateTimeField(auto_now=True,
                                    verbose_name="date modified")

    class Meta:
        ordering = ["start"]

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('events:detail',
                       kwargs={
                           'year': self.start.year,
                           'slug': self.slug
                       })

    @property
    def is_ended(self):
        if self.end:
            end = datetime.combine(self.end, self.end_time) if self.end_time else\
                datetime.combine(self.end, datetime.min.time(), tzinfo=utc)
        else:
            end = datetime.combine(
                self.start,
                self.end_time) if self.end_time else (self.start +
                                                      timedelta(days=1))

        return end < now()

    @cached_property
    def structured_data(self):
        url = settings.SITE_URL + self.get_absolute_url()
        data = {
            '@type':
            'Event',
            'name':
            self.title,
            'description':
            self.description
            or Truncator(strip_tags(self.description_rendered)).chars(200),
            'startDate':
            self.start.strftime('%Y-%m-%d'),
            'url':
            url,
            'mainEntityOfPage': {
                '@type': 'WebPage',
                '@id': url
            },
        }

        if self.end:
            data['endDate'] = self.end.strftime('%Y-%m-%d')

        if self.location:
            data['location'] = {
                '@type': 'Place',
                'name': self.location,
                'address': self.address or self.location,
            }

        if self.image:
            data['image'] = {
                '@type': 'ImageObject',
                'url': self.image.url,
                'height': self.image.height,
                'width': self.image.width
            }

        return data
Beispiel #19
0
class ProjectActivity(models.Model):
    """
    The Project Activity model holds data for specific sustainability activities associated with a Project.

    The model acts as a container for both target activities and potentially alternative "baseline candidates"

    A Project will have at least one Project Activity

    project_activity_identifier      varchar(80),
    project_activity_description     text, -> SHORT_DESCR
    project_activity_emissions       real,
    baseline_activity_emissions      real,
    project_activity_role            integer,
    baseline_estimation              integer,
    baseline_procedure_justification text,

    references portfolio_project

    NEW project_activity_title
    NEW region
    NEW main_site


    """
    # IDENTIFICATION

    project_activity_identifier = models.CharField(
        max_length=80,
        blank=True,
        null=True,
        help_text=
        'A unique identification of a Project Activity for internal use')

    project_activity_title = models.CharField(
        max_length=160,
        blank=True,
        null=True,
        help_text='The title of the project activity')

    project_activity_description = MarkdownField(
        blank=True,
        null=True,
        rendered_field='text_rendered',
        validator=VALIDATOR_STANDARD,
        help_text=
        'Textual description of a Project Activity. Markdown format is supported <a class="risk_manual_url" href="https://www.openriskmanual.org/wiki/GHG_Project_Activity">Documentation</a>'
    )

    text_rendered = RenderedMarkdownField()

    # LINKS

    project = models.ForeignKey(
        'Project',
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        help_text="The Project to which this Activity belongs")

    # PROJECT ACTIVITY DATA

    region = models.CharField(
        max_length=10,
        null=True,
        blank=True,
        help_text=
        'NUTS Code of Region. <a class="risk_manual_url" href="https://www.openriskmanual.org/wiki">Documentation</a>'
    )

    main_site = models.CharField(
        max_length=160,
        blank=True,
        null=True,
        help_text='The main site of the project activity (if applicable)')

    project_activity_emissions = models.FloatField(
        blank=True,
        null=True,
        help_text="Emissions expressed in t CO2 eq/year")

    baseline_activity_emissions = models.FloatField(
        blank=True,
        null=True,
        help_text="Emissions expressed in t CO2 eq/year")

    project_activity_role = models.IntegerField(
        blank=True,
        null=True,
        choices=PROJECT_ACTIVITY_ROLE,
        help_text='Select whether the activity role is baseline or target')

    baseline_estimation = models.IntegerField(
        blank=True,
        null=True,
        choices=BASELINE_ESTIMATION_PROCEDURE,
        help_text=
        'Baseline procedures are methods used to estimate baseline emissions <a class="risk_manual_url" href="https://www.openriskmanual.org/wiki/Baseline_Emissions">Documentation</a>'
    )

    baseline_procedure_justification = MarkdownField(
        blank=True,
        null=True,
        rendered_field='text_rendered2',
        validator=VALIDATOR_STANDARD,
        help_text=
        'Justification of the Baseline Estimation Procedure selected <a class="risk_manual_url" href="https://www.openriskmanual.org/wiki/Baseline_Emissions">Documentation</a>'
    )

    # text = MarkdownField(rendered_field='text_rendered', validator=VALIDATOR_STANDARD)
    text_rendered2 = RenderedMarkdownField()

    #
    # BOOKKEEPING FIELDS
    #
    creation_date = models.DateTimeField(auto_now_add=True)
    last_change_date = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.project_activity_identifier

    def get_absolute_url(self):
        return reverse('portfolio:ProjectActivity_edit',
                       kwargs={'pk': self.pk})

    class Meta:
        verbose_name = "Project Activity"
        verbose_name_plural = "Project Activities"
Beispiel #20
0
class Project(models.Model):
    """
    The Project model holds data for a general sustainability Project (irrespective of financial attributes)


    """

    # TODO Project Status

    # IDENTIFICATION

    project_identifier = models.CharField(max_length=80, blank=True, null=True,
                                          help_text='A unique identification of the Project for internal use')

    project_title = models.CharField(max_length=160, blank=True, null=True,
                                     help_text='The title of the project')

    project_reference = models.CharField(max_length=160, blank=True, null=True,
                                         help_text='Manager reference for the project')

    # LINKS

    project_category = models.ForeignKey('ProjectCategory', blank=True, null=True, on_delete=models.SET_NULL,
                                         help_text="The project category to which this project is best classified")

    portfolio = models.ForeignKey('ProjectPortfolio', blank=True, null=True, on_delete=models.CASCADE,
                                  help_text="The portfolio to which this project belongs")

    # PROJECT DATA

    project_description = MarkdownField(blank=True, null=True, rendered_field='text_rendered',
                                        validator=VALIDATOR_STANDARD,
                                        help_text='Textual description of a Project. Markdown format is supported <a class="risk_manual_url" href="https://www.openriskmanual.org/wiki/GHG_Project">Documentation</a>')

    # text = MarkdownField(rendered_field='text_rendered', validator=VALIDATOR_STANDARD)
    text_rendered = RenderedMarkdownField()

    cpv_code = models.CharField(max_length=20, blank=True, null=True,
                                help_text="The Common Procurement Vocabulary Code")

    project_budget = models.IntegerField(blank=True, null=True, help_text="The Project Budget")

    project_currency = models.CharField(max_length=4, blank=True, null=True,
                                        help_text="The currency code in which the project is accounted for")

    # PROJECT SCORECARD DATA

    design_and_technology_risk = models.IntegerField(blank=True, null=True, choices=DESIGN_AND_TECHNOLOGY_RISK_CHOICES,
                                                     help_text='Risk Factor. EBA 3.1. <a class="risk_manual_url" href="https://www.openriskmanual.org/wiki/Technology_Risk">Documentation</a>')

    completion_risk = models.IntegerField(blank=True, null=True, choices=COMPLETION_RISK_CHOICES,
                                          help_text='Risk SubFactor. EBA 3.2.3. <a class="risk_manual_url" href="https://www.openriskmanual.org/wiki">Documentation</a>')

    # OTHER

    construction_risk = models.FloatField(blank=True, null=True,
                                          help_text='Standard Description. <a class="risk_manual_url" href="https://www.openriskmanual.org/wiki">Documentation</a>')

    project_visualization = models.ImageField(upload_to='project_files', blank=True, null=True,
                                              help_text='Visual representation of a  Project')
    #
    # BOOKKEEPING FIELDS
    #
    creation_date = models.DateTimeField(auto_now_add=True)
    last_change_date = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.project_identifier

    def get_absolute_url(self):
        return reverse('portfolio:Project_edit', kwargs={'pk': self.pk})

    class Meta:
        verbose_name = "Project"
        verbose_name_plural = "Projects"