def test_markdownfield_widget_instance(): field = MarkdownField() widget_instance = MarkdownTextarea(attrs={'rows': 30, 'autofocus': True}) form_field = field.formfield(widget=widget_instance) assert isinstance(form_field, MarkdownFormField) assert isinstance(form_field.widget, MarkdownTextarea) assert form_field.widget.attrs['rows'] == 30 assert form_field.widget.attrs['autofocus'] is True
class CollectionItem(models.Model): content_type = models.ForeignKey(ContentType, models.CASCADE, limit_choices_to=model_choices) object_id = models.PositiveIntegerField( help_text='Numeric id of the object.') content_object = GenericForeignKey('content_type', 'object_id') collection = models.ForeignKey('website.Collection', models.CASCADE, related_name='items') order = models.PositiveIntegerField(blank=True, null=True) description = MarkdownField(blank=True, help_text='Teaser/description. Keep it short.') description_rendered = models.TextField(blank=True, editable=False) class Meta: ordering = ['collection_id', 'order'] def __str__(self): return '{} {}: "{}"'.format(self.content_type.model.title(), self.object_id, str(self.content_object)) def save(self, *args, **kwargs): self.description_rendered = render_markdown(self.description) super(CollectionItem, self).save(*args, **kwargs)
class Person(Publishable): """Describes a person""" identifier = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) # Name info given_name = models.CharField('First name', max_length=100, help_text="A person's given or 'first' name(s).") additional_name = models.CharField('Middle name', blank=True, max_length=100, help_text="An additional name for a person, \ such as a 'middle' name.") family_name = models.CharField('Last name', blank=True, max_length=140, help_text="A person's family or 'last' name(s).") image = FilerImageField(blank=True, null=True, help_text='Preferably a portrait/"head shot".') # Biographical Info description = MarkdownField('Bio/Description', blank=True) description_rendered = models.TextField(blank=True, editable=False) citizenships = models.ManyToManyField('locations.Country', blank=True) birth_year = models.PositiveSmallIntegerField(blank=True, null=True) birth_month = models.PositiveSmallIntegerField(blank=True, null=True) birth_day = models.PositiveSmallIntegerField(blank=True, null=True) # Notes notes = MarkdownField(blank=True) # Relations events = models.ManyToManyField('Event', blank=True) class Meta: ordering = ['family_name', 'given_name', 'additional_name'] verbose_name_plural = 'people' def __str__(self): return " ".join((self.given_name, self.family_name)) def full_display_name(self): name_parts = (self.given_name, self.additional_name or None, self.family_name) return " ".join([x for x in name_parts if x]) full_display_name.short_description = "Full name" def get_absolute_url(self): return reverse('facts:person-detail', kwargs={ 'slug': slugify(self.full_display_name()), 'identifier': str(self.identifier) }) def save(self, *args, **kwargs): self.description_rendered = render_markdown(self.description) super(Person, self).save(*args, **kwargs)
class LegalTextVersion(models.Model): legaltext = models.ForeignKey(LegalText, verbose_name=_('Legal text'), on_delete=models.CASCADE) valid_from = models.DateTimeField(_('Valid from'), default=timezone.now) content = MarkdownField( _('Text'), help_text=_( 'You can use [block:foo]Your text[/block] to create a block with an anchor. ' 'Anchors ([anchor:foo]) can be used in checkbox texts to link to ' 'specific parts of the legal text.')) class Meta: verbose_name = _('Legal text version') verbose_name_plural = _('Legal text versions') ordering = ('legaltext__slug', '-valid_from') def __str__(self): return '{0} ({1:%x %X})'.format(self.legaltext.name, timezone.localtime(self.valid_from)) @property def name(self): return self.legaltext.name def render_content(self): anchor_class = getattr(settings, 'LEGALTEXT_ANCHOR_CLASS', None) def block_nl_callback(match): return '\n\n{0}\n\n'.format(match.group(1)) def block_open_callback(match): block_open_callback.count += 1 return ('<div class="legaltext-block legaltext-block-{0}">' '<span id="{0}"{1}></span>').format( match.group(1), ' class="{0}"'.format(anchor_class) if anchor_class else '', ) block_open_callback.count = 0 def block_close_callback(match): block_close_callback.count += 1 return '</div>' block_close_callback.count = 0 content = BLOCK_OPEN_NL_RE.sub(block_nl_callback, self.content) content = BLOCK_CLOSE_NL_RE.sub(block_nl_callback, content) content = render_markdown(content) content = BLOCK_OPEN_RE.sub(block_open_callback, content) content = BLOCK_CLOSE_RE.sub(block_close_callback, content) if block_open_callback.count == block_close_callback.count: return content return render_markdown(self.content)
class Event(Publishable): """Describes an event, one which may have a start and end date""" name = models.CharField(max_length=100) slug = models.SlugField(max_length=110, allow_unicode=True, unique=True) event_type = models.ForeignKey('EventType', models.SET_NULL, verbose_name='type', blank=True, null=True) description = MarkdownField(blank=True) description_rendered = models.TextField(blank=True, editable=False) start_year = models.PositiveSmallIntegerField(blank=True, null=True) start_month = models.PositiveSmallIntegerField(blank=True, null=True) start_day = models.PositiveSmallIntegerField(blank=True, null=True) @property def fuzzy_start_date(self): return fuzzydate(self.start_year, self.start_month, self.start_day) end_year = models.PositiveSmallIntegerField(blank=True, null=True) end_month = models.PositiveSmallIntegerField(blank=True, null=True) end_day = models.PositiveSmallIntegerField(blank=True, null=True) @property def fuzzy_end_date(self): return fuzzydate(self.end_year, self.end_month, self.end_day) places = models.ManyToManyField('locations.Place', blank=True) documents = models.ManyToManyField('sources.Document', blank=True) notes = MarkdownField(blank=True) def __str__(self): return self.name def get_absolute_url(self): return reverse('facts:event-detail', args=[self.slug]) def save(self, *args, **kwargs): self.description_rendered = render_markdown(self.description) if not self.slug or self.slug == '': self.slug = slugify(self.name, allow_unicode=True) super(Event, self).save(*args, **kwargs) class Meta: ordering = ['created_at']
class Post(models.Model): content = MarkdownField(_('Content')) class Meta: verbose_name = _('Post') verbose_name_plural = _('Posts') def __unicode__(self): return u'{0}'.format(self.id)
class LegalTextCheckbox(models.Model): legaltext_version = models.ForeignKey(LegalTextVersion, verbose_name=_('Legal text version'), related_name='checkboxes', on_delete=models.CASCADE) content = MarkdownField( _('Text'), help_text= _('You can use [anchor]Your text[/anchor] to create a link to the full legal text. ' 'If you use [anchor:foo]Your text[/anchor] the link will go to the block "foo" ' 'inside the legal text.')) order = models.PositiveIntegerField(_('Order'), blank=True, null=True) class Meta: verbose_name = _('Legal text checkbox') verbose_name_plural = _('Legal text Checkboxes') ordering = ('legaltext_version', 'order') unique_together = (('legaltext_version', 'order'), ) def __str__(self): return gettext('Checkbox for {0} ({1})').format( self.legaltext_version.name, '{0:%x %X}'.format( timezone.localtime(self.legaltext_version.valid_from))) def render_content(self): def anchor_callback(match): return '<a href="{0}{1}" class="legaltext-anchor" target="_blank">{2}</a>'.format( self.legaltext_version.legaltext.get_absolute_url(), '#{0}'.format(match.group(1)) if match.group(1) else '', match.group(2)) content = ANCHOR_RE.sub( anchor_callback, normalize_newlines(self.content).replace('\n', '<br />')) return render_markdown(content).replace('<p>', '').replace('</p>', '') def save(self, *args, **kwargs): if not self.order: self.order = (self.legaltext_version.checkboxes.aggregate( next_order=models.Max('order'))['next_order'] or 0) + 1 super().save(*args, **kwargs)
class Entry(Publishable): """An entry in a 'blog' or whatever you want to call it.""" title = models.CharField(max_length=100) subtitle = models.CharField(max_length=100, blank=True) slug = models.SlugField(max_length=110, allow_unicode=True, unique=True) author = models.CharField('Author(s)', blank=True, max_length=100) content = MarkdownField(blank=True) content_rendered = models.TextField(blank=True, editable=False) description = MarkdownField( 'Summary/Description', blank=True, help_text='Short text to be used where post might be promoted/referenced. Limited to 400 characters.' ) description_rendered = models.TextField(blank=True, editable=False) share_text = models.CharField(blank=True, max_length=140) featured_image = FilerImageField(blank=True, null=True) is_sponsored = models.BooleanField(default=False) sponsored_logo = FilerImageField(blank=True, null=True, related_name='sponsored_article') publication_date = models.DateTimeField( blank=True, null=True, help_text="""<p>Publication date will be set to the current time automatically, when <em>published</em> is selected. <p>You may set a date/time manually, but <strong>you must select <em>published</em> for the post to appear!</strong> """ ) categories = models.ManyToManyField( Category, blank=True, related_name='entries', ) tags = TaggableManager(blank=True) related_entries = models.ManyToManyField('self', blank=True) class Meta: ordering = ("-publication_date", "-created_at") get_latest_by = "publication_date" verbose_name = "entry" verbose_name_plural = "entries" def __str__(self): return self.title def save(self, *args, **kwargs): if self.published and not self.publication_date: self.publication_date = timezone.now() self.content_rendered = render_markdown(self.content) self.description_rendered = render_markdown(self.description) super(Entry, self).save(*args, **kwargs) def get_absolute_url(self): return reverse( 'writings:entry-detail', kwargs={ 'slug': self.slug, } ) def is_visible(self): return self.published and self.publication_date <= timezone.now() def get_next_published_entry(self): if not self.publication_date: return None # If no publication date query = Q(publication_date__gt=self.publication_date) query |= Q(publication_date=self.publication_date, pk__gt=self.pk) qs = self.__class__.objects.published().filter(query).order_by('publication_date', 'pk') try: return qs[0] except IndexError: return None def get_previous_published_entry(self): if not self.publication_date: return None # If no publication date query = Q(publication_date__lt=self.publication_date) query |= Q(publication_date=self.publication_date, pk__lt=self.pk) qs = self.__class__.objects.published().filter(query).order_by('-publication_date', '-pk') try: return qs[0] except IndexError: return None
class Organization(MPTTModel, Publishable): """Abstract base model for organizations""" identifier = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) name = models.CharField(max_length=100) slug = models.SlugField(max_length=110, allow_unicode=True) countries = models.ManyToManyField('locations.Country', blank=True) leaders = models.ManyToManyField('Person', blank=True, related_name='organizations_led') initiatives = models.ManyToManyField('infrastructure.Initiative', blank=True) headquarters = models.ForeignKey('locations.Place', models.SET_NULL, blank=True, null=True) description = MarkdownField(blank=True) description_rendered = models.TextField(blank=True, editable=False) notes = MarkdownField(blank=True) related_events = models.ManyToManyField('Event', blank=True) founding_year = models.PositiveSmallIntegerField(blank=True, null=True) founding_month = models.PositiveSmallIntegerField(blank=True, null=True) founding_day = models.PositiveSmallIntegerField(blank=True, null=True) @property def fuzzy_founding_date(self): return fuzzydate(self.founding_year, self.founding_month, self.founding_day) dissolution_year = models.PositiveSmallIntegerField(blank=True, null=True) dissolution_month = models.PositiveSmallIntegerField(blank=True, null=True) dissolution_day = models.PositiveSmallIntegerField(blank=True, null=True) @property def fuzzy_dissolution_date(self): return fuzzydate(self.dissolution_year, self.dissolution_month, self.dissolution_day) parent = TreeForeignKey('self', models.CASCADE, null=True, blank=True, verbose_name='parent organization', related_name='children', db_index=True) staff_size = models.PositiveIntegerField("Staff/Personnel count", blank=True, null=True) mission = MarkdownField("Mandate/Mission Statement", blank=True) mission_rendered = models.TextField(blank=True, editable=False) related_organizations = models.ManyToManyField('self', blank=True) documents = models.ManyToManyField('sources.Document', blank=True) publishable_objects = PublishableQuerySet.as_manager() class MPTTMeta: order_insertion_by = ['name'] class Meta: verbose_name_plural = "all organizations" ordering = ['name', 'created_at'] def __str__(self): return self.name def get_absolute_url(self): return reverse('facts:organization-detail', kwargs={ 'slug': self.slug, 'identifier': str(self.identifier) }) def get_detail_types(self): return { model_name: label for model_name, label in DETAIL_MODEL_NAMES.items() if hasattr(self, model_name) } def get_organization_types(self): return frozenset([ label for model_name, label in DETAIL_MODEL_NAMES.items() if hasattr(self, model_name) ]) def save(self, *args, **kwargs): self.description_rendered = render_markdown(self.description) self.mission_rendered = render_markdown(self.mission) if not self.slug or self.slug == '': self.slug = slugify(self.name, allow_unicode=True) super(Organization, self).save(*args, **kwargs)
def test_markdownfield_formfield(): field = MarkdownField() form_field = field.formfield() assert isinstance(form_field, MarkdownFormField) assert isinstance(form_field.widget, MarkdownTextarea)
class Project(Publishable): """Describes a project""" identifier = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) name = models.CharField("Project name/title", max_length=300) slug = models.SlugField(max_length=310, allow_unicode=True) alternate_name = models.CharField( blank=True, max_length=100, help_text='Alternate name for project, suitable for display as a header' ) alternate_slug = models.SlugField( blank=True, max_length=110, allow_unicode=True ) description = MarkdownField(blank=True) description_rendered = models.TextField(blank=True, editable=False) countries = models.ManyToManyField('locations.Country', blank=True) regions = models.ManyToManyField( 'locations.Region', blank=True, help_text='Select or create geographic region names.' ) infrastructure_type = models.ForeignKey( InfrastructureType, models.SET_NULL, blank=True, null=True, help_text='Select or create named infrastructure types.' ) total_cost = models.BigIntegerField( blank=True, null=True, help_text="Values in whole units (dollars, etc.)" ) total_cost_currency = models.CharField( blank=True, null=True, max_length=3, choices=CURRENCY_CHOICES, default=DEFAULT_CURRENCY_CHOICE ) start_year = models.PositiveSmallIntegerField(blank=True, null=True) start_month = models.PositiveSmallIntegerField(blank=True, null=True) start_day = models.PositiveSmallIntegerField(blank=True, null=True) @property def fuzzy_start_date(self): return fuzzydate(self.start_year, self.start_month, self.start_day) commencement_year = models.PositiveSmallIntegerField('Year of commencement of works', blank=True, null=True) commencement_month = models.PositiveSmallIntegerField('Month of commencement of works', blank=True, null=True) commencement_day = models.PositiveSmallIntegerField('Day of commencement of works', blank=True, null=True) @property def fuzzy_commencement_date(self): return fuzzydate(self.commencement_year, self.commencement_month, self.commencement_day) planned_completion_year = models.PositiveSmallIntegerField(blank=True, null=True) planned_completion_month = models.PositiveSmallIntegerField(blank=True, null=True) planned_completion_day = models.PositiveSmallIntegerField(blank=True, null=True) @property def fuzzy_planned_completion_date(self): return fuzzydate(self.planned_completion_year, self.planned_completion_month, self.planned_completion_day) status = models.PositiveSmallIntegerField( blank=True, null=True, choices=ProjectStatus.STATUSES, default=ProjectStatus.ANNOUNCED ) new = models.NullBooleanField('New Construction?') initiatives = models.ManyToManyField('Initiative', blank=True) documents = models.ManyToManyField('ProjectDocument', blank=True) sources = ArrayField( models.CharField(max_length=1000, validators=[URLLikeValidator]), blank=True, null=True, default=list, verbose_name="Sources URLs", help_text='Enter URLs separated by commas.' ) notes = MarkdownField(blank=True) # Organization relations contractors = models.ManyToManyField( 'facts.Organization', verbose_name='Contractors', related_name='projects_contracted', blank=True ) consultants = models.ManyToManyField( 'facts.Organization', verbose_name='Consultants', related_name='projects_consulted', blank=True ) implementers = models.ManyToManyField( 'facts.Organization', verbose_name='Client or implementing agency/ies', related_name='projects_implemented', blank=True ) operators = models.ManyToManyField( 'facts.Organization', related_name='projects_operated', blank=True, ) # Person relations contacts = models.ManyToManyField( 'facts.Person', verbose_name='Points of contact', related_name='projects_contacts', blank=True ) # Extras & Internal Use extra_data = models.ManyToManyField('facts.Data', blank=True) verified_path = models.NullBooleanField(blank=True) collection_stage = models.PositiveSmallIntegerField( blank=True, null=True, choices=CollectionStage.STAGES, default=CollectionStage.IDENTIFIED ) # Geodata geo = models.ForeignKey( 'locations.GeometryStore', models.SET_NULL, blank=True, null=True, related_name='projects' ) class Meta: ordering = ['name'] def save(self, *args, **kwargs): self.description_rendered = render_markdown(self.description) super(Project, self).save(*args, **kwargs) def __str__(self): return self.name def get_absolute_url(self): return reverse( 'infrastructure:project-detail', kwargs={ 'slug': self.alternate_slug or self.slug or 'p', 'identifier': str(self.identifier) } )
class ProjectDocument(models.Model): DOCUMENT_TYPES = ( ('Public Materials', ( (1, 'Press Releases'), (2, 'Presentations & Brochures'), (3, 'National Development Plans'), )), ('Agreements/Contracts', ( (4, 'MoU'), (5, 'Financing Agreements'), (6, 'Procurement Contracts'), (7, 'Other Agreements'), )), ('Operational Documents', ( (8, 'Concept Notes'), (9, 'Review and Approval Documents'), (10, 'Procurement Documents'), (11, 'Appraisal Documents'), (12, 'Administration Manuals'), (13, 'Aide-Memoires'), (14, 'Financial Audits'), )), ('Impact Assessment and Monitoring Reports', ( (15, 'Environmental and Social Assessment'), (16, 'Resettlement Frameworks'), (17, 'Safeguards Monitoring Reports'), (18, 'Consultation Minutes'), )), ('Implementation Progress Reports', ( (19, 'Progress Reports'), (20, 'Completion Reports'), )), ('Miscellaneous Reports', ( (21, 'Miscellaneous Reports'), )), ('Unofficial Sources', ( (22, 'Unofficial Sources'), )), ) identifier = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) document = models.ForeignKey( 'sources.Document', models.SET_NULL, blank=True, null=True ) source_url = models.CharField(blank=True, max_length=1000, validators=[URLLikeValidator]) document_type = models.PositiveSmallIntegerField( 'type', choices=DOCUMENT_TYPES, blank=True, null=True ) notes = MarkdownField(blank=True) status_indicator = models.PositiveSmallIntegerField( blank=True, null=True, choices=ProjectStatus.STATUSES ) class Meta: ordering = ['document_type', 'source_url'] def __str__(self): if self.source_url: return self.source_url return "Document #{}".format(self.id) def get_source_url_tail(self): return self.source_url.split('/')[-1] or None @classmethod def _flattened_doc_types(cls): return (value for _, subset in cls.DOCUMENT_TYPES for value in subset) @classmethod def get_document_type_names(cls): return tuple(name for _, name in cls._flattened_doc_types()) @classmethod def lookup_document_type_by_name(cls, name): if name: filtered = filter(lambda x: x[1].lower() == name.lower(), cls._flattened_doc_types()) as_list = list(filtered) if len(as_list) == 1: return as_list[0] return None
class PowerPlant(Publishable): """Describes a Power Plant""" name = models.CharField(max_length=140) slug = models.SlugField(max_length=150, allow_unicode=True) description = MarkdownField(blank=True) description_rendered = models.TextField(blank=True, editable=False) infrastructure_type = models.ForeignKey( InfrastructureType, models.SET_NULL, blank=True, null=True, help_text='Select or create named insfrastructure types.', ) fuels = models.ManyToManyField('Fuel', blank=True) total_cost = models.BigIntegerField( blank=True, null=True, help_text="Values in whole units (dollars, etc.)") total_cost_currency = models.CharField(blank=True, max_length=3, choices=CURRENCY_CHOICES, default=DEFAULT_CURRENCY_CHOICE) countries = models.ManyToManyField('locations.Country', blank=True) regions = models.ManyToManyField( 'locations.Region', blank=True, help_text='Select or create geographic region names.') status = models.PositiveSmallIntegerField( blank=True, null=True, choices=PowerPlantStatus.STATUSES) plant_year_online = models.PositiveSmallIntegerField(blank=True, null=True) plant_month_online = models.PositiveSmallIntegerField(blank=True, null=True) plant_day_online = models.PositiveSmallIntegerField(blank=True, null=True) plant_initiatives = models.ManyToManyField('Initiative', blank=True) @property def fuzzy_plant_online_date(self): return fuzzydate(self.plant_year_online, self.plant_month_online, self.plant_day_online) decommissioning_year = models.PositiveSmallIntegerField(blank=True, null=True) decommissioning_month = models.PositiveSmallIntegerField(blank=True, null=True) decommissioning_day = models.PositiveSmallIntegerField(blank=True, null=True) @property def fuzzy_decommissioning_date(self): return fuzzydate(self.decommissioning_year, self.decommissioning_month, self.decommissioning_day) plant_capacity = models.FloatField(blank=True, null=True) plant_capacity_unit = models.PositiveSmallIntegerField( blank=True, null=True, choices=ProjectPlantUnits.UNITS) plant_output = models.FloatField(blank=True, null=True) plant_output_unit = models.PositiveSmallIntegerField( blank=True, null=True, choices=ProjectPlantUnits.UNITS) plant_output_year = models.PositiveSmallIntegerField(blank=True, null=True) estimated_plant_output = models.FloatField(blank=True, null=True) estimated_plant_output_unit = models.PositiveSmallIntegerField( blank=True, null=True, choices=ProjectPlantUnits.UNITS) plant_CO2_emissions = models.FloatField(blank=True, null=True) plant_CO2_emissions_unit = models.PositiveSmallIntegerField( blank=True, null=True, choices=ProjectPlantUnits.UNITS) grid_connected = models.NullBooleanField('Grid connected?') notes = MarkdownField(blank=True) # Geodata geo = models.ForeignKey( 'locations.GeometryStore', models.SET_NULL, blank=True, null=True, related_name='power_plants', ) operators = models.ManyToManyField('facts.Organization', verbose_name='Operators', related_name='plants_operated', blank=True) sources = ArrayField( models.CharField(max_length=1000, validators=[URLLikeValidator]), blank=True, null=True, default=list, verbose_name="Sources URLs", help_text='Enter URLs separated by commas.', ) class Meta: ordering = ['name'] def __str__(self): return self.name def get_absolute_url(self): return reverse('infrastructure:powerplant-detail', kwargs={'slug': self.slug}) def save(self, *args, **kwargs): self.description_rendered = render_markdown(self.description) return super().save(*args, **kwargs)
def test_markdownfield_formfield_no_override(): field = MarkdownField() form_field = field.formfield(widget=CustomMarkdownTextarea) assert isinstance(form_field, MarkdownFormField) assert isinstance(form_field.widget, CustomMarkdownTextarea)
class Initiative(MPTTModel, Publishable): """Describes an initiative""" identifier = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) name = models.CharField(max_length=140) slug = models.SlugField(max_length=150, allow_unicode=True) initiative_type = models.ForeignKey('InitiativeType', models.SET_NULL, blank=True, null=True) notes = MarkdownField(blank=True) principal_agent = models.ForeignKey( 'facts.Organization', models.SET_NULL, blank=True, null=True, related_name='principal_initiatives', ) parent = TreeForeignKey('self', null=True, blank=True, verbose_name='parent initiative', related_name='children', db_index=True) related_initiatives = models.ManyToManyField('self', blank=True) documents = models.ManyToManyField('sources.Document', blank=True) founding_year = models.PositiveSmallIntegerField('Founding/Signing Year', blank=True, null=True) founding_month = models.PositiveSmallIntegerField('Founding/Signing Month', blank=True, null=True) founding_day = models.PositiveSmallIntegerField('Founding/Signing Day', blank=True, null=True) @property def fuzzy_founding_date(self): return fuzzydate(self.founding_year, self.founding_month, self.founding_day) affiliated_organizations = models.ManyToManyField('facts.Organization', blank=True) affiliated_events = models.ManyToManyField('facts.Event', blank=True) affiliated_people = models.ManyToManyField('facts.Person', blank=True) member_countries = models.ManyToManyField( 'locations.Country', blank=True, related_name='initiatives_joined' ) geographic_scope = models.ManyToManyField( 'locations.Country', blank=True, related_name='initiatives_in_scope' ) appeared_year = models.PositiveSmallIntegerField('First Apperance Year', blank=True, null=True) appeared_month = models.PositiveSmallIntegerField('First Apperance Month', blank=True, null=True) appeared_day = models.PositiveSmallIntegerField('First Apperance Day', blank=True, null=True) @property def fuzzy_appeared_date(self): return fuzzydate(self.appeared_year, self.appeared_month, self.appeared_day) class Meta: ordering = ['name'] publishable_objects = PublishableQuerySet.as_manager() def __str__(self): return self.name def save(self, *args, **kwargs): if not self.slug or self.slug == '': self.slug = slugify(self.name, allow_unicode=True) super(Initiative, self).save(*args, **kwargs) def get_absolute_url(self): return reverse( 'infrastructure:initiative-detail', kwargs={ 'slug': self.slug or 'i', 'identifier': str(self.identifier) } )
class Project(Publishable): """Describes a project""" # ---- STANDARD FIELDS ---- # identifier = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) name = models.CharField("Project name/title", max_length=300) slug = models.SlugField(max_length=310, allow_unicode=True) alternate_name = models.CharField( blank=True, max_length=100, help_text= 'Alternate name for project, suitable for display as a header', ) alternate_slug = models.SlugField(blank=True, max_length=110, allow_unicode=True) description = MarkdownField(blank=True) description_rendered = models.TextField(blank=True, editable=False) countries = models.ManyToManyField('locations.Country', blank=True) regions = models.ManyToManyField( 'locations.Region', blank=True, help_text='Select or create geographic region names.') infrastructure_type = models.ForeignKey( InfrastructureType, models.SET_NULL, blank=True, null=True, help_text='Select or create named infrastructure types.', ) total_cost = models.BigIntegerField( blank=True, null=True, help_text="Values in whole units (dollars, etc.)") total_cost_currency = models.CharField( blank=True, null=True, max_length=3, choices=CURRENCY_CHOICES, default=DEFAULT_CURRENCY_CHOICE, ) start_year = models.PositiveSmallIntegerField(blank=True, null=True) start_month = models.PositiveSmallIntegerField(blank=True, null=True) start_day = models.PositiveSmallIntegerField(blank=True, null=True) commencement_year = models.PositiveSmallIntegerField( 'Year of commencement of works', blank=True, null=True) commencement_month = models.PositiveSmallIntegerField( 'Month of commencement of works', blank=True, null=True) commencement_day = models.PositiveSmallIntegerField( 'Day of commencement of works', blank=True, null=True) planned_completion_year = models.PositiveSmallIntegerField(blank=True, null=True) planned_completion_month = models.PositiveSmallIntegerField(blank=True, null=True) planned_completion_day = models.PositiveSmallIntegerField(blank=True, null=True) linear_length = models.PositiveSmallIntegerField(blank=True, null=True, help_text="km") new = models.NullBooleanField('New Construction?') initiatives = models.ManyToManyField('Initiative', blank=True) documents = models.ManyToManyField('ProjectDocument', blank=True) sources = ArrayField( models.CharField(max_length=1000, validators=[URLLikeValidator]), blank=True, null=True, default=list, verbose_name="Sources URLs", help_text='Enter URLs separated by commas.', ) notes = MarkdownField(blank=True) # Organization relations contractors = models.ManyToManyField( 'facts.Organization', verbose_name='Contractors', related_name='projects_contracted', blank=True, ) manufacturers = models.ManyToManyField( 'facts.Organization', verbose_name='Manufacturers', related_name='projects_manufactured', blank=True, ) consultants = models.ManyToManyField( 'facts.Organization', verbose_name='Consultants', related_name='projects_consulted', blank=True, ) implementers = models.ManyToManyField( 'facts.Organization', verbose_name='Client or implementing agency/ies', related_name='projects_implemented', blank=True, ) operators = models.ManyToManyField('facts.Organization', related_name='projects_operated', blank=True) # Person relations contacts = models.ManyToManyField( 'facts.Person', verbose_name='Points of contact', related_name='projects_contacts', blank=True, ) # Extras & Internal Use extra_data = models.ManyToManyField('facts.Data', blank=True) verified_path = models.NullBooleanField(blank=True) collection_stage = models.PositiveSmallIntegerField( blank=True, null=True, choices=CollectionStage.STAGES, default=CollectionStage.IDENTIFIED) # Geodata geo = models.ForeignKey('locations.GeometryStore', models.SET_NULL, blank=True, null=True, related_name='projects') status = models.PositiveSmallIntegerField(blank=True, null=True, choices=ProjectStatus.STATUSES, default=ProjectStatus.ANNOUNCED) # ---- End STANDARD FIELDS ---- # # ---- POWER PLANT TYPE PROJECT FIELDS ---- # power_plant = models.ForeignKey('PowerPlant', models.CASCADE, blank=True, null=True) fuels = models.ManyToManyField('Fuel', blank=True) # These are ostensibly for all projects but they make the most sense # for power plants project_output = models.FloatField(blank=True, null=True) project_output_unit = models.PositiveSmallIntegerField( blank=True, null=True, choices=ProjectPlantUnits.UNITS) project_output_year = models.PositiveSmallIntegerField(blank=True, null=True) estimated_project_output = models.FloatField(blank=True, null=True) estimated_project_output_unit = models.PositiveSmallIntegerField( blank=True, null=True, choices=ProjectPlantUnits.UNITS) project_capacity = models.FloatField(blank=True, null=True) project_capacity_unit = models.CharField( blank=True, default='', max_length=20, choices=ProjectCapacityUnits.UNITS) project_capacity_timeframe = models.CharField( blank=True, max_length=10, choices=ProjectTimeFrameUnits.TIME_UNITS) project_CO2_emissions = models.FloatField(blank=True, null=True) project_CO2_emissions_unit = models.PositiveSmallIntegerField( blank=True, null=True, choices=ProjectPlantUnits.UNITS) nox_reduction_system = models.NullBooleanField('NOx Reduction System?') sox_reduction_system = models.NullBooleanField('SOx Reduction System?') # ---- END POWER PLANT TYPE PROJECT FIELDS ---- # # ---- TRANSMISSION TYPE PROJECT FIELDS ---- # design_voltage = models.BigIntegerField(null=True, blank=True, help_text="Design Voltage (kV)") direct_current = models.NullBooleanField(null=True, blank=True, help_text="Direct Current?") electricity_flow = models.CharField( null=True, blank=True, max_length=15, choices=(('bidirectional', 'Bidirectional'), ('unidirectional', 'Unidirectional')), help_text="Electricity Flow (direction)", ) estimated_transfer_capacity = models.BigIntegerField( null=True, blank=True, help_text="Estimated Transfer Capacity (MW)") # ---- END TRANSMISSION TYPE PROJECT FIELDS ---- # # ---- PIPELINE TYPE PROJECT FIELDS ---- # pipeline_diameter = models.IntegerField("Pipeline Diameter", null=True, blank=True) pipeline_diameter_unit = models.CharField("Diameter Unit", blank=True, max_length=6, choices=PipelineDiameters.UNITS, help_text="Diameter unit") pipeline_metered = models.NullBooleanField("Pipeline Metered?", null=True, blank=True) pipeline_throughput = models.BigIntegerField("Throughput", null=True, blank=True) pipeline_throughput_unit = models.CharField( "Throughput Unit", blank=True, max_length=20, choices=ProjectThroughputUnits.UNITS) pipeline_throughput_timeframe = models.CharField( "Throughput Timeframe", blank=True, max_length=10, choices=ProjectTimeFrameUnits.TIME_UNITS) pipeline_throughput_year = models.PositiveSmallIntegerField( "Throughput Year", null=True, blank=True) # ---- END PIPELINE TYPE PROJECT FIELDS ---- # # ---- PROPERTIES ---- # @property def fuzzy_output_date(self): return fuzzydate(self.project_output_year) @property def fuzzy_start_date(self): return fuzzydate(self.start_year, self.start_month, self.start_day) @property def fuzzy_commencement_date(self): return fuzzydate(self.commencement_year, self.commencement_month, self.commencement_day) @property def fuzzy_planned_completion_date(self): return fuzzydate(self.planned_completion_year, self.planned_completion_month, self.planned_completion_day) @property def humanize_capacity(self): if self.project_capacity is None: return False if self.project_capacity >= (10** 6): # Is the capacity GTE to 1 million return True return False @property def pipeline_capacity_property(self): if self.project_capacity is None: return None pc = '' if self.project_capacity_unit: pc += " {}".format( self.get_project_capacity_unit_display().lower()) if self.project_capacity_timeframe: pc += " {}".format(self.get_project_capacity_timeframe_display()) return pc @property def pipeline_throughput_property(self): if self.pipeline_throughput is None: return None pt = '' if self.pipeline_throughput_unit: pt += " {}".format( self.get_pipeline_throughput_unit_display().lower()) if self.pipeline_throughput_timeframe: pt += " {}".format( self.get_pipeline_throughput_timeframe_display()) if self.pipeline_throughput_year: pt += " ({})".format(self.pipeline_throughput_year) return pt # ---- END PROPERTIES ---- # class Meta: ordering = ['name'] def save(self, *args, **kwargs): self.description_rendered = render_markdown(self.description) super(Project, self).save(*args, **kwargs) def __str__(self): return self.name def get_absolute_url(self): return reverse( 'infrastructure:project-detail', kwargs={ 'slug': self.alternate_slug or self.slug or 'p', 'identifier': str(self.identifier), }, )