Merge remote-tracking branch 'origin/master'

This commit is contained in:
Weblate 2018-04-04 16:41:40 +00:00
commit 06b5a6d290
35 changed files with 1711 additions and 411 deletions

3
.gitignore vendored
View File

@ -105,3 +105,6 @@ environment
# Editor swap # Editor swap
*.swp *.swp
# Docker stuff
.containers

View File

@ -1,12 +1,33 @@
from django.contrib import admin from django.contrib import admin
from moderation.admin import ModerationAdmin
from leaflet.admin import LeafletGeoAdmin from leaflet.admin import LeafletGeoAdmin
from .models import CaseStudy from .models import CaseStudy
class CaseStudyAdmin(LeafletGeoAdmin): class CaseStudyAdmin(LeafletGeoAdmin):
pass list_display = ('id', 'date_created', 'entry_name', 'approved')
actions = ['approve', 'unapprove']
def approve(self, request, queryset):
updated = queryset.update(approved=True)
if updated == 1:
message_bit = "1 case study was"
else:
message_bit = "{0} case studies were".format(updated)
self.message_user(request, "{0} successfully approved".format(
message_bit
))
approve.short_description = "Approve selected case studies"
def unapprove(self, request, queryset):
updated = queryset.update(approved=False)
if updated == 1:
message_bit = "1 case study was"
else:
message_bit = "{0} case studies were".format(updated)
self.message_user(request, "{0} successfully un-approved".format(
message_bit
))
unapprove.short_description = "Un-approve selected case studies"
admin.site.register(CaseStudy, CaseStudyAdmin) admin.site.register(CaseStudy, CaseStudyAdmin)

View File

@ -1,20 +1,20 @@
from django.urls import reverse from django.urls import reverse
from django import forms from django import forms
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout, HTML, Fieldset from crispy_forms.layout import Submit, Layout, HTML, Fieldset, Div
from crispy_forms.bootstrap import Tab, TabHolder, PrependedText, FormActions from crispy_forms.bootstrap import Tab, TabHolder, PrependedText, FormActions
from leaflet.forms.widgets import LeafletWidget from leaflet.forms.widgets import LeafletWidget
from moderation.forms import BaseModeratedObjectForm
from .models import CaseStudy from .models import CaseStudy
class BaseCaseStudyForm(BaseModeratedObjectForm): class BaseCaseStudyForm(forms.models.ModelForm):
"""Base form class for the CaseStudy model.""" """Base form class for the CaseStudy model."""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(BaseCaseStudyForm, self).__init__(*args, **kwargs) super(BaseCaseStudyForm, self).__init__(*args, **kwargs)
self.helper = FormHelper() self.helper = FormHelper(self)
self.helper.form_id = 'case-study-form' self.helper.form_id = 'case-study-form'
self.helper.form_class = 'form-horizontal' self.helper.form_class = 'form-horizontal'
self.helper.form_method = 'post' self.helper.form_method = 'post'
@ -39,6 +39,7 @@ class ShortCaseStudyForm(BaseCaseStudyForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ShortCaseStudyForm, self).__init__(*args, **kwargs) super(ShortCaseStudyForm, self).__init__(*args, **kwargs)
self.helper.form_action = reverse('short-form') self.helper.form_action = reverse('short-form')
self.helper.add_input(Submit('submit', _('Submit'), css_class='btn-success center-block'))
class Meta(BaseCaseStudyForm.Meta): class Meta(BaseCaseStudyForm.Meta):
fields = [ fields = [
@ -53,8 +54,8 @@ class ShortCaseStudyForm(BaseCaseStudyForm):
'location_context', 'location_context',
'type_of_ecosystem', 'type_of_ecosystem',
'describe_ecosystem', 'describe_ecosystem',
'affects_indigenous', 'people_affected_indigenous',
'affects_indigenous_detail', 'people_affected_other',
'project_status', 'project_status',
'synopsis', 'synopsis',
'full_description', 'full_description',
@ -67,19 +68,61 @@ class ShortCaseStudyForm(BaseCaseStudyForm):
'community_voices' 'community_voices'
] ]
class LongCaseStudyForm(BaseCaseStudyForm): class LongCaseStudyForm(BaseCaseStudyForm):
"""Long version of the CaseStudy form.""" """Long version of the CaseStudy form."""
POSITIVE_CASE_TYPE_HELP = {
'CREP': _("We are using the World Wind Energy Association's Community Power definition, \
which is that a community project is one where at least \
two of the following three criteria are met:<br> \
1. Local stakeholders own the majority or all of a project,<br> \
2. Voting control rests with the community-based organization,<br> \
3. The majority of social and economic benefits are distributed locally."),
'EACP': _(""),
'PSEP': _(""),
'CORS': _("The extraction of non-renewable resources, such as iron, copper, \
rare-earth elements or other minerals and metals used in \
renewable technologies, directly from the Earth is by definition \
an unsustainable practice. Despite this, the extraction of such elements \
this way for use in the renewable energy transition is, to an extent, \
a necessary evil in the immediate term. Bearing this in mind, \
a case involving extraction may be considered 'positive' if it helps \
to reduce, overall, the need for more extraction; if it drastically \
reduces ecological harms often caused by mining and does not infringe \
on areas of high biodiversity; and if it meets outstanding social \
and human rights standards that are enjoyed and affirmed by \
host communities and other stakeholders. Such social standards include: \
ensuring communities, and especially indigenous peoples, \
enjoy their right to Free Prior and Informed Consent, \
which includes the right to reject projects; abiding by \
the UN's guiding principles on Business and Human Rights; \
full collaboration with the Extractive Industries Transparency Initiative, \
assuring excellence in the transparency of project financing, \
tax affairs and other transactions; the highest labour standards; \
equitable distribution of any benefits accruing from mining; \
respect for the rule of law and the constitutional rights of citizens \
in host countries."),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(LongCaseStudyForm, self).__init__(*args, **kwargs) super(LongCaseStudyForm, self).__init__(*args, **kwargs)
POSITIVE_CASE_TYPE_CHOICES = [
(choice[0], mark_safe('<b>%s</b><br><span class="text-muted">%s</span>' % (choice[1], self.POSITIVE_CASE_TYPE_HELP[choice[0]])))
for choice in CaseStudy.POSITIVE_CASE_TYPE_CHOICES
]
self.fields['positive_case_type'] = forms.ChoiceField(
widget=forms.RadioSelect(),
choices=POSITIVE_CASE_TYPE_CHOICES
)
self.helper.form_action = reverse('long-form') self.helper.form_action = reverse('long-form')
self.helper.layout = Layout( self.helper.layout = Layout(
TabHolder( TabHolder(
Tab(_("Basic information"), Tab(_("Basic information"),
'entry_name', 'entry_name',
'location', 'location',
'sector_of_economy',
'positive_or_negative',
'country', 'country',
'area_of_land', 'area_of_land',
'land_ownership', 'land_ownership',
@ -87,32 +130,40 @@ class LongCaseStudyForm(BaseCaseStudyForm):
'location_context', 'location_context',
'type_of_ecosystem', 'type_of_ecosystem',
'describe_ecosystem', 'describe_ecosystem',
'affects_indigenous', 'people_affected_indigenous',
'affects_indigenous_detail', 'people_affected_other',
'project_status', 'project_status',
'synopsis', 'synopsis',
'full_description', 'full_description',
'project_owners',
'shareholders',
'financial_institutions',
'energy_customers',
'image', 'image',
'image_caption', 'image_caption',
'image_credit', 'image_credit',
'video', 'video',
'video_caption', 'video_caption',
'video_credit', 'video_credit',
'media_coverage_mainstream', Fieldset(
'media_coverage_independent', _("Ownership and finance"),
'community_voices', 'project_owners',
'direct_comms', 'shareholders',
'social_media_links', 'financial_institutions',
'financial_institutions_other',
'energy_customers'
),
Fieldset(
_("Media reports and other communications"),
'media_coverage_mainstream',
'media_coverage_independent',
'community_voices',
'direct_comms',
'social_media_links'
),
FormActions( FormActions(
HTML("<a class='btn btn-primary btnNext pull-right'>"+_("Next")+"</a>") HTML("<a class='btn btn-primary btnNext pull-right'>"+_("Next")+"</a>")
) )
), ),
Tab( Tab(
_("Technical and economic analysis"), _("Technical and economic analysis"),
'sector_of_economy',
Fieldset( Fieldset(
_("Power Generation Questions"), _("Power Generation Questions"),
'generation_technology', 'generation_technology',
@ -156,6 +207,7 @@ class LongCaseStudyForm(BaseCaseStudyForm):
), ),
Tab( Tab(
_("Socio-environmental analysis"), _("Socio-environmental analysis"),
'positive_or_negative',
Fieldset( Fieldset(
_("Positive Case Questions"), _("Positive Case Questions"),
'positive_case_type', 'positive_case_type',
@ -178,8 +230,7 @@ class LongCaseStudyForm(BaseCaseStudyForm):
'wants_conversation_with_ojuso', 'wants_conversation_with_ojuso',
css_id="negative_case_questions" css_id="negative_case_questions"
), ),
Fieldset( Div(
_("Common Questions"),
'key_actors_involved', 'key_actors_involved',
css_id="common_questions" css_id="common_questions"
), ),
@ -188,6 +239,19 @@ class LongCaseStudyForm(BaseCaseStudyForm):
HTML("<a class='btn btn-primary btnNext pull-right'>"+_("Next")+"</a>") HTML("<a class='btn btn-primary btnNext pull-right'>"+_("Next")+"</a>")
) )
), ),
Tab(
_("Contact details"),
'contact_email',
'contact_phone',
'contact_website',
PrependedText('contact_twitter', '@', placeholder='username'),
'contact_facebook',
'contact_other',
FormActions(
HTML("<a class='btn btn-primary btnPrevious'>"+_("Previous")+"</a>"),
HTML("<a class='btn btn-primary btnNext pull-right'>"+_("Next")+"</a>")
)
),
Tab( Tab(
_("Uploads"), _("Uploads"),
'official_project_documents', 'official_project_documents',
@ -204,4 +268,4 @@ class LongCaseStudyForm(BaseCaseStudyForm):
))) )))
class Meta(BaseCaseStudyForm.Meta): class Meta(BaseCaseStudyForm.Meta):
fields = '__all__' exclude = ('approved',)

View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-26 01:57
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('map', '0034_auto_20171103_2254'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='completion_year',
field=models.IntegerField(blank=True, choices=[(1978, 1978), (1979, 1979), (1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024), (2025, 2025), (2026, 2026), (2027, 2027), (2028, 2028), (2029, 2029), (2030, 2030), (2031, 2031), (2032, 2032), (2033, 2033), (2034, 2034), (2035, 2035), (2036, 2036), (2037, 2037), (2038, 2038), (2039, 2039), (2040, 2040), (2041, 2041), (2042, 2042), (2043, 2043), (2044, 2044), (2045, 2045), (2046, 2046), (2047, 2047), (2048, 2048), (2049, 2049), (2050, 2050), (2051, 2051), (2052, 2052), (2053, 2053), (2054, 2054), (2055, 2055), (2056, 2056), (2057, 2057), (2058, 2058)], default=None, help_text="Select the year the project was completed. If the project hasn't finished, select the projected completion year.", null=True, verbose_name='Completion year'),
),
migrations.AlterField(
model_name='casestudy',
name='shown_on_other_platforms',
field=models.BooleanField(default=False, help_text='Tick the box if you would like us to show this case study on other social media platforms', verbose_name='Show on other platforms?'),
preserve_default=False,
),
migrations.AlterField(
model_name='casestudy',
name='start_year',
field=models.IntegerField(blank=True, choices=[(1978, 1978), (1979, 1979), (1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024), (2025, 2025), (2026, 2026), (2027, 2027), (2028, 2028), (2029, 2029), (2030, 2030), (2031, 2031), (2032, 2032), (2033, 2033), (2034, 2034), (2035, 2035), (2036, 2036), (2037, 2037), (2038, 2038), (2039, 2039), (2040, 2040), (2041, 2041), (2042, 2042), (2043, 2043), (2044, 2044), (2045, 2045), (2046, 2046), (2047, 2047), (2048, 2048), (2049, 2049), (2050, 2050), (2051, 2051), (2052, 2052), (2053, 2053), (2054, 2054), (2055, 2055), (2056, 2056), (2057, 2057), (2058, 2058)], default=None, help_text="Select the year the project was started. If the project hasn't begun, select the projected start year.", null=True, verbose_name='Start year'),
),
migrations.AlterField(
model_name='casestudy',
name='wants_conversation_with_ojuso',
field=models.BooleanField(default=True, help_text='This would be a conversation about challenging or engaging related developers, companies and investors.', verbose_name='Would you like to have a conversation with the ojuso team?'),
),
]

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-27 03:34
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('map', '0035_auto_20180326_0157'),
]
operations = [
migrations.AlterModelOptions(
name='casestudy',
options={'verbose_name_plural': 'case studies'},
),
migrations.AddField(
model_name='casestudy',
name='approved',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-27 05:49
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('map', '0036_auto_20180327_0334'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='direct_comms',
field=models.TextField(blank=True, default=None, help_text='Add any reports of direct communication between community members and representatives of developers/companies/investors.', max_length=500, null=True, verbose_name='Reports of direct communications'),
),
migrations.AlterField(
model_name='casestudy',
name='projected_production_of_commodities',
field=models.CharField(blank=True, default=None, help_text="Describe the projected production of commodities per annum and overall (e.g. '40 million tonnes of iron ore per year, 200 million tonnes over 5 year life of mine'", max_length=256, null=True, verbose_name='Projected production of key commodities'),
),
migrations.AlterField(
model_name='casestudy',
name='size_of_concessions',
field=models.CharField(blank=True, default=None, help_text="Describe the size of concession(s) granted to company/companies (e.g. 'one concession encompassing 2,300 hectares')", max_length=200, null=True, verbose_name='Size of concessions'),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-28 01:46
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('map', '0037_auto_20180327_0549'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='sector_of_economy',
field=models.CharField(choices=[('RN', 'Renewable Energy Generation'), ('PG', 'Power Grids'), ('SM', 'Supply of Minerals')], help_text='Which sector of the renewable energy economy is most relevant?', max_length=3, verbose_name='Sector of economy'),
),
]

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-28 02:45
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('map', '0038_auto_20180328_0146'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='type_of_extraction',
field=models.CharField(blank=True, choices=[('SUR', 'Surface (open pit/open cast/open cut mining'), ('SUB', 'Sub-surface (underground mining)'), ('SEA', 'Seabed mining'), ('URB', 'Urban mining/recycling')], default=None, max_length=3, null=True, verbose_name='Type of extraction'),
),
migrations.AlterField(
model_name='casestudy',
name='use_in_energy_economy',
field=models.CharField(blank=True, choices=[('WTM', 'Wind turbine manufacturing'), ('SPM', 'Solar panel manufacturing'), ('STM', 'Solar thermal system manufacturing'), ('HGM', 'Hydropower generator manufacturing'), ('GGM', 'Geothermal generator manufacturing'), ('ESS', 'Energy storage (inc. battery systems)'), ('OTR', 'Others')], default=None, help_text='Select the potential use of the minerals in the renewable energy economy', max_length=3, null=True, verbose_name='Potential use in renewable energy economy'),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-28 03:09
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('map', '0039_auto_20180328_0245'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='type_of_extraction',
field=models.CharField(blank=True, choices=[('SUR', 'Surface (open pit/open cast/open cut mining)'), ('SUB', 'Sub-surface (underground mining)'), ('SEA', 'Seabed mining'), ('URB', 'Urban mining/recycling')], default=None, max_length=3, null=True, verbose_name='Type of extraction'),
),
]

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-28 06:16
from __future__ import unicode_literals
from django.db import migrations
import multiselectfield.db.fields
class Migration(migrations.Migration):
dependencies = [
('map', '0040_auto_20180328_0309'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='negative_case_reasons',
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('VOLR', 'Violation of land rights'), ('VOHR', 'Violation of fundamental human rights, indigenous rights and/or other collective rights'), ('EIMP', 'Environmental impacts (severe impacts on ecosystems / violation of laws, plans or programs of environmental conservation or territorial governance systems etc.'), ('NCUL', 'Negative cultural impacts (erosion/destruction of bio-cultural heritage, impacts on sacred land etc)'), ('AGGR', 'Aggression/threats to community members opposed to the project, collaboration with organized crime etc'), ('ALAB', 'Abusive labour practices'), ('CRUP', 'Corruption and/or irregular permitting or contracting, conflicts of interest etc'), ('OTHR', 'Other reasons')], default=None, max_length=39, null=True, verbose_name='Reasons this is a negative case study'),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-28 11:22
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('map', '0041_auto_20180328_0616'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='shown_on_other_platforms',
field=models.BooleanField(default=True, help_text='Tick the box if you would like us to show this case study on other social media platforms', verbose_name='Show on other platforms?'),
),
]

View File

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-29 10:44
from __future__ import unicode_literals
import apps.map.validators
from django.db import migrations, models
import multiselectfield.db.fields
class Migration(migrations.Migration):
dependencies = [
('map', '0042_auto_20180328_1122'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='community_voices',
field=models.TextField(blank=True, default=None, help_text='Add any direct quotes from members of the community that relate to this project', null=True, verbose_name='Community Voices'),
),
migrations.AlterField(
model_name='casestudy',
name='direct_comms',
field=models.TextField(blank=True, default=None, help_text='Add any reports of direct communication between community members and representatives of developers/companies/investors.', null=True, verbose_name='Reports of direct communications'),
),
migrations.AlterField(
model_name='casestudy',
name='discharge_time',
field=models.DecimalField(blank=True, decimal_places=3, default=None, help_text='Enter the time it takes to discharge from full capacity at maximum power output (in hours).', max_digits=6, null=True, verbose_name='Time for discharge from full capacity'),
),
migrations.AlterField(
model_name='casestudy',
name='energy_storage_capacity',
field=models.DecimalField(blank=True, decimal_places=3, default=None, help_text='Enter the total capacity of the energy storage system in kilowatt-hours (kWh).', max_digits=20, null=True, verbose_name='Energy storage capacity'),
),
migrations.AlterField(
model_name='casestudy',
name='image_caption',
field=models.CharField(default=None, max_length=240, null=True, verbose_name='Image caption'),
),
migrations.AlterField(
model_name='casestudy',
name='image_credit',
field=models.CharField(default=None, max_length=240, null=True, verbose_name='Image credit(s)'),
),
migrations.AlterField(
model_name='casestudy',
name='location_context',
field=models.CharField(choices=[('RUR', 'Rural'), ('URB', 'Urban'), ('MIX', 'Mixed')], help_text='Select the context that is most applicable to this case study.', max_length=3, verbose_name='Location'),
),
migrations.AlterField(
model_name='casestudy',
name='maximum_power_output',
field=models.DecimalField(blank=True, decimal_places=3, default=None, help_text='Enter the maximum power output of the storage system in kilowatts (kW).', max_digits=12, null=True, verbose_name='Maximum power output'),
),
migrations.AlterField(
model_name='casestudy',
name='media_coverage_independent',
field=models.TextField(default=None, help_text='Provide any links to grassroots/independent media coverage.', null=True, verbose_name='Independent grassroots reports'),
),
migrations.AlterField(
model_name='casestudy',
name='media_coverage_mainstream',
field=models.TextField(default=None, help_text='Provide any links to mainstream media coverage.', null=True, verbose_name='Links to media reports'),
),
migrations.AlterField(
model_name='casestudy',
name='type_of_ecosystem',
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('Water Based', (('MARINE', 'Marine (e.g. Ocean, Sea)'), ('FRESH', 'Freshwater (e.g. Freshwater, Lake)'))), ('Land Based', (('FOREST', 'Forest/Jungle'), ('AGRI', 'Agricultural Land'), ('GRASS', 'Grassland'), ('DESERT', 'Desert (Tundra, Ice or Sand)'), ('WETLND', 'Wetland (Marsh, Mangrove, Peat Soil)'), ('URBAN', 'Urban')))], default=None, help_text='Select the most relevant type of ecosystem.', max_length=6, null=True, verbose_name='Type of ecosystem'),
),
migrations.AlterField(
model_name='casestudy',
name='video',
field=models.URLField(blank=True, default=None, help_text='Copy the URL to a related YouTube™ video that relates to the case study.', max_length=43, null=True, validators=[apps.map.validators.YoutubeURLValidator()], verbose_name='YouTube Video'),
),
migrations.AlterField(
model_name='casestudy',
name='video_caption',
field=models.CharField(blank=True, default=None, max_length=240, null=True, verbose_name='Video caption'),
),
migrations.AlterField(
model_name='casestudy',
name='video_credit',
field=models.CharField(blank=True, default=None, max_length=240, null=True, verbose_name='Video credit(s)'),
),
]

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-31 04:59
from __future__ import unicode_literals
from django.db import migrations, models
import phonenumber_field.modelfields
class Migration(migrations.Migration):
dependencies = [
('map', '0043_auto_20180329_1044'),
]
operations = [
migrations.AddField(
model_name='casestudy',
name='contact_email',
field=models.EmailField(blank=True, max_length=254, verbose_name='Email address'),
),
migrations.AddField(
model_name='casestudy',
name='contact_facebook',
field=models.URLField(blank=True, verbose_name='Facebook page'),
),
migrations.AddField(
model_name='casestudy',
name='contact_other',
field=models.TextField(blank=True, verbose_name='Other contact details'),
),
migrations.AddField(
model_name='casestudy',
name='contact_phone',
field=phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, verbose_name='Phone number'),
),
migrations.AddField(
model_name='casestudy',
name='contact_twitter',
field=models.CharField(blank=True, max_length=50, verbose_name='Twitter username'),
),
migrations.AddField(
model_name='casestudy',
name='contact_website',
field=models.URLField(blank=True, verbose_name='Website'),
),
]

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-31 05:17
from __future__ import unicode_literals
from django.db import migrations, models
import phonenumber_field.modelfields
class Migration(migrations.Migration):
dependencies = [
('map', '0044_auto_20180331'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='contact_phone',
field=phonenumber_field.modelfields.PhoneNumberField(blank=True, help_text='Please include the international prefix, beginning with "+".', max_length=128, verbose_name='Phone number'),
),
migrations.AlterField(
model_name='casestudy',
name='shown_on_other_platforms',
field=models.BooleanField(default=True, verbose_name='Is this case study shown on other platforms?'),
),
migrations.AlterField(
model_name='casestudy',
name='shown_on_other_platforms_detail',
field=models.TextField(blank=True, default='', help_text='Please provide links to other places the case study appears.', verbose_name='Shown on other platforms - Detail'),
preserve_default=False,
),
]

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-31 06:04
from __future__ import unicode_literals
import apps.map.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('map', '0045_auto_20180331_0517'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='video',
field=models.URLField(blank=True, default='', help_text='Copy the URL to a YouTube or Vimeo video that relates to the case study.', max_length=80, validators=[apps.map.validators.YouTubeOrVimeoValidator()], verbose_name='Video URL'),
preserve_default=False,
),
]

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-31 06:08
from __future__ import unicode_literals
from django.db import migrations
import multiselectfield.db.fields
class Migration(migrations.Migration):
dependencies = [
('map', '0046_auto_20180331_0604'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='type_of_ecosystem',
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('MARINE', 'Marine (e.g. Ocean, Sea)'), ('FRESH', 'Freshwater (e.g. Freshwater, Lake)'), ('FOREST', 'Forest/Jungle'), ('AGRI', 'Agricultural Land'), ('GRASS', 'Grassland'), ('DESERT', 'Desert (Tundra, Ice or Sand)'), ('WETLND', 'Wetland (Marsh, Mangrove, Peat Soil)'), ('URBAN', 'Urban')], default=None, help_text='Select the most relevant type of ecosystem.', max_length=6, null=True, verbose_name='Type of ecosystem'),
),
migrations.AlterField(
model_name='casestudy',
name='type_of_ecosystem',
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('MARINE', 'Marine (e.g. Ocean, Sea)'), ('FRESH', 'Freshwater (e.g. Freshwater, Lake)'), ('FOREST', 'Forest/Jungle'), ('AGRI', 'Agricultural Land'), ('GRASS', 'Grassland'), ('DESERT', 'Desert (Tundra, Ice or Sand)'), ('WETLND', 'Wetland (Marsh, Mangrove, Peat Soil)'), ('URBAN', 'Urban')], default=None, help_text='Select the most relevant type(s).', max_length=6, null=True, verbose_name='Type(s) of ecosystem'),
),
]

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-31 09:33
from __future__ import unicode_literals
from django.db import migrations, models
import multiselectfield.db.fields
class Migration(migrations.Migration):
dependencies = [
('map', '0047_auto_20180331_0607'),
]
operations = [
migrations.AddField(
model_name='casestudy',
name='financial_institutions_other',
field=models.TextField(blank=True, help_text='List any other financial institutions not listed above. Put each on a new line.', verbose_name='Financial institutions other'),
),
migrations.AlterField(
model_name='casestudy',
name='financial_institutions',
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('AfDB', 'African Development Bank (AfDB)'), ('BADEA', 'Arab Bank for Economic Development in Africa (BADEA)'), ('ADB', 'Asian Development Bank (ADB)'), ('AIIB', 'Asian Infrastructure Investment Bank (AIIB)'), ('BSTDB', 'Black Sea Trade and Development Bank (BSTDB)'), ('CAF', 'Corporacion Andina de Fomento / Development Bank of Latin America (CAF)'), ('CDB', 'Caribbean Development Bank (CDB)'), ('CABEI', 'Central American Bank for Economic Integration (CABEI)'), ('EADB', 'East African Development Bank (EADB)'), ('ETDB', 'Economic Cooperation Organization Trade and Development Bank (ETDB)'), ('EDB', 'Eurasian Development Bank (EDB)'), ('EBRD', 'European Bank for Reconstruction and Development (EBRD)'), ('EC', 'European Commission (EC)'), ('EIB', 'European Investment Bank (EIB)'), ('IADB', 'Inter-American Development Bank Group (IDB, IADB)'), ('IFFIm', 'International Finance Facility for Immunisation (IFFIm)'), ('IFAD', 'International Fund for Agricultural Development (IFAD)'), ('IIB', 'International Investment Bank (IIB)'), ('IsDB', 'Islamic Development Bank (IsDB)'), ('FMO', 'Nederlandse Financieringsmaatschappij voor Ontwikkelingslanden NV (FMO)'), ('NDB', 'New Development Bank (NDB) (formerly BRICS Development Bank)'), ('NDF', 'The Nordic Development Fund'), ('NIB', 'Nordic Investment Bank (NIB)'), ('OFID', 'OPEC Fund for International Development (OFID)'), ('BOAD', 'West African Development Bank (BOAD)'), ('WB', 'World Bank')], default='', help_text='Select any financial institutions that have or are considering extending loans or guarantees to the project.', max_length=119, verbose_name='Financial institutions'),
preserve_default=False,
),
]

View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-31 11:34
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('map', '0048_auto_20180331_0933'),
]
operations = [
migrations.RemoveField(
model_name='casestudy',
name='affects_indigenous',
),
migrations.RemoveField(
model_name='casestudy',
name='affects_indigenous_detail',
),
migrations.AddField(
model_name='casestudy',
name='people_affected_indigenous',
field=models.TextField(blank=True, help_text='What group or groups of indigenous people are affected by this project? Please separate by newline.', verbose_name='Indigenous people affected'),
),
migrations.AddField(
model_name='casestudy',
name='people_affected_other',
field=models.TextField(blank=True, help_text='What other group or groups of people are affected by this project? Please separate by newline.', verbose_name='Non-indigenous people affected'),
),
]

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-04-02 12:37
from __future__ import unicode_literals
from django.db import migrations
import multiselectfield.db.fields
class Migration(migrations.Migration):
dependencies = [
('map', '0049_auto_20180331_1134'),
]
operations = [
migrations.AlterField(
model_name='casestudy',
name='type_of_ecosystem',
field=multiselectfield.db.fields.MultiSelectField(blank=True, choices=[('MARINE', 'Marine (e.g. Ocean, Sea)'), ('FRESH', 'Freshwater (e.g. Freshwater, Lake)'), ('FOREST', 'Forest/Jungle'), ('AGRI', 'Agricultural Land'), ('GRASS', 'Grassland'), ('DESERT', 'Desert (Tundra, Ice or Sand)'), ('WETLND', 'Wetland (Marsh, Mangrove, Peat Soil)'), ('URBAN', 'Urban')], default=None, help_text='Select the most relevant type(s).', max_length=56, null=True, verbose_name='Type(s) of ecosystem'),
),
]

View File

@ -7,6 +7,8 @@ from django_countries.fields import CountryField
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from multiselectfield import MultiSelectField from multiselectfield import MultiSelectField
from phonenumber_field.modelfields import PhoneNumberField
from . import validators from . import validators
@ -16,16 +18,21 @@ class Shapefile(models.Model):
) )
class CaseStudyQuerySet(models.QuerySet):
def approved(self):
return self.filter(
approved=True
)
class CaseStudy(models.Model): class CaseStudy(models.Model):
"""Model for case studies submitted to the Ojuso Platform""" """Model for case studies submitted to the Ojuso Platform"""
approved = models.BooleanField(default=False)
# Choice lists for drop-downs # Choice lists for drop-downs
SECTOR_CHOICES = ( SECTOR_CHOICES = (
(_('Renewable Energy Generation'), ( ('RN', _('Renewable Energy Generation')),
('WND', _('Wind')),
('SOL', _('Solar')),
('HYD', _('Hydro')),
)),
('PG', _('Power Grids')), ('PG', _('Power Grids')),
('SM', _('Supply of Minerals')), ('SM', _('Supply of Minerals')),
) )
@ -45,21 +52,18 @@ class CaseStudy(models.Model):
LOCATION_CONTEXT_CHOICES = ( LOCATION_CONTEXT_CHOICES = (
('RUR', _('Rural')), ('RUR', _('Rural')),
('URB', _('Urban')), ('URB', _('Urban')),
('MIX', _('Mixed')),
) )
TYPE_OF_ECOSYSTEM_CHOICES = ( TYPE_OF_ECOSYSTEM_CHOICES = (
(_('Water Based'), ( ('MARINE', _('Marine (e.g. Ocean, Sea)')),
('MARINE', _('Marine (e.g. Ocean, Sea)')), ('FRESH', _('Freshwater (e.g. Freshwater, Lake)')),
('FRESH', _('Freshwater (e.g. Freshwater, Lake)')), ('FOREST', _('Forest/Jungle')),
)), ('AGRI', _('Agricultural Land')),
(_('Land Based'), ( ('GRASS', _('Grassland')),
('FOREST', _('Forest/Jungle')), ('DESERT', _('Desert (Tundra, Ice or Sand)')),
('AGRI', _('Agricultural Land')), ('WETLND', _('Wetland (Marsh, Mangrove, Peat Soil)')),
('GRASS', _('Grassland')), ('URBAN', _('Urban'))
('DESERT', _('Desert (Tundra, Ice or Sand)')),
('WETLND', _('Wetland (Marsh, Mangrove, Peat Soil)')),
('URBAN', _('Urban')),
))
) )
PROJECT_STATUS_CHOICES = ( PROJECT_STATUS_CHOICES = (
@ -68,6 +72,35 @@ class CaseStudy(models.Model):
('PROJCD', _('Projected Project')), ('PROJCD', _('Projected Project')),
) )
FINANCIAL_INSTITUTIONS = (
('AfDB', _('African Development Bank (AfDB)')),
('BADEA', _('Arab Bank for Economic Development in Africa (BADEA)')),
('ADB', _('Asian Development Bank (ADB)')),
('AIIB', _('Asian Infrastructure Investment Bank (AIIB)')),
('BSTDB', _('Black Sea Trade and Development Bank (BSTDB)')),
('CAF', _('Corporacion Andina de Fomento / Development Bank of Latin America (CAF)')),
('CDB', _('Caribbean Development Bank (CDB)')),
('CABEI', _('Central American Bank for Economic Integration (CABEI)')),
('EADB', _('East African Development Bank (EADB)')),
('ETDB', _('Economic Cooperation Organization Trade and Development Bank (ETDB)')),
('EDB', _('Eurasian Development Bank (EDB)')),
('EBRD', _('European Bank for Reconstruction and Development (EBRD)')),
('EC', _('European Commission (EC)')),
('EIB', _('European Investment Bank (EIB)')),
('IADB', _('Inter-American Development Bank Group (IDB, IADB)')),
('IFFIm', _('International Finance Facility for Immunisation (IFFIm)')),
('IFAD', _('International Fund for Agricultural Development (IFAD)')),
('IIB', _('International Investment Bank (IIB)')),
('IsDB', _('Islamic Development Bank (IsDB)')),
('FMO', _('Nederlandse Financieringsmaatschappij voor Ontwikkelingslanden NV (FMO)')),
('NDB', _('New Development Bank (NDB) (formerly BRICS Development Bank)')),
('NDF', _('The Nordic Development Fund')),
('NIB', _('Nordic Investment Bank (NIB)')),
('OFID', _('OPEC Fund for International Development (OFID)')),
('BOAD', _('West African Development Bank (BOAD)')),
('WB', _('World Bank')),
)
GENERATION_TECHNOLOGY_CHOICES = ( GENERATION_TECHNOLOGY_CHOICES = (
(_('Wind energy'), ( (_('Wind energy'), (
('SSWE', _('Small-scale (less than 500kW)')), ('SSWE', _('Small-scale (less than 500kW)')),
@ -96,7 +129,7 @@ class CaseStudy(models.Model):
) )
TYPE_OF_EXTRACTION_CHOICES = ( TYPE_OF_EXTRACTION_CHOICES = (
('SUR', _('Surface (open pit/open cast/open cut mining')), ('SUR', _('Surface (open pit/open cast/open cut mining)')),
('SUB', _('Sub-surface (underground mining)')), ('SUB', _('Sub-surface (underground mining)')),
('SEA', _('Seabed mining')), ('SEA', _('Seabed mining')),
('URB', _('Urban mining/recycling')) ('URB', _('Urban mining/recycling'))
@ -156,6 +189,8 @@ class CaseStudy(models.Model):
('CORS', _('A case of responsible sourcing/supply chain/lifecycle management')), ('CORS', _('A case of responsible sourcing/supply chain/lifecycle management')),
) )
NEGATIVE_CASE_REASONS_OTHER_TEXT = _('Other reasons')
NEGATIVE_CASE_REASONS_CHOICES = ( NEGATIVE_CASE_REASONS_CHOICES = (
('VOLR', _('Violation of land rights')), ('VOLR', _('Violation of land rights')),
('VOHR', _('Violation of fundamental human rights, indigenous rights and/or other collective rights')), ('VOHR', _('Violation of fundamental human rights, indigenous rights and/or other collective rights')),
@ -167,7 +202,7 @@ class CaseStudy(models.Model):
etc')), etc')),
('ALAB', _('Abusive labour practices')), ('ALAB', _('Abusive labour practices')),
('CRUP', _('Corruption and/or irregular permitting or contracting, conflicts of interest etc')), ('CRUP', _('Corruption and/or irregular permitting or contracting, conflicts of interest etc')),
('OTHR', _('Other reasons')) ('OTHR', NEGATIVE_CASE_REASONS_OTHER_TEXT)
) )
# Dynamically generate a list of choices 40 years prior and after the current year. # Dynamically generate a list of choices 40 years prior and after the current year.
@ -269,10 +304,10 @@ class CaseStudy(models.Model):
) )
# 1.5.5 # 1.5.5
type_of_ecosystem = models.CharField( type_of_ecosystem = MultiSelectField(
verbose_name=_("Type of ecosystem"), verbose_name=_("Type(s) of ecosystem"),
help_text=_("Select the most relevant type of ecosystem."), help_text=_("Select the most relevant type(s)."),
max_length=6, max_length=56,
choices=TYPE_OF_ECOSYSTEM_CHOICES, choices=TYPE_OF_ECOSYSTEM_CHOICES,
default=None, default=None,
null=True, null=True,
@ -285,20 +320,18 @@ class CaseStudy(models.Model):
help_text=_("In your own words, add more detail about the ecosystem."), help_text=_("In your own words, add more detail about the ecosystem."),
) )
# 1.5.6 # Was 1.5.6; spec not being followed here after request from client
affects_indigenous = models.BooleanField( people_affected_indigenous = models.TextField(
verbose_name=_("Affects indigenous people?"), verbose_name=_("Indigenous people affected"),
help_text=_("Does the project affect indigenous communities?") help_text=_("What group or groups of indigenous people are affected by this project? \
Please separate by newline."),
blank=True
) )
# 1.5.6.1 people_affected_other = models.TextField(
affects_indigenous_detail = models.CharField( verbose_name=_("Non-indigenous people affected"),
verbose_name=_("Affects Indigenous - Details"), help_text=_("What other group or groups of people are affected by this project? \
help_text=_("What group of indigenous people does the community belong\ Please separate by newline."),
to?"),
max_length=256,
default=None,
null=True,
blank=True blank=True
) )
@ -368,14 +401,20 @@ class CaseStudy(models.Model):
blank=True blank=True
) )
# 1.13 # 1.13.1
financial_institutions = models.CharField( financial_institutions = MultiSelectField(
verbose_name=_("Financial institutions"), verbose_name=_("Financial institutions"),
help_text=_("List banks and other financial institutions that have or are considering extending loans \ help_text=_("Select any financial institutions that have or are considering extending \
or guarantees to the project. Separate with a comma."), loans or guarantees to the project."),
max_length=120, choices=FINANCIAL_INSTITUTIONS,
default=None, blank=True
null=True, )
# 1.13.2
financial_institutions_other = models.TextField(
verbose_name=_("Financial institutions other"),
help_text=_("List any other financial institutions not listed above. \
Put each on a new line."),
blank=True blank=True
) )
@ -398,7 +437,7 @@ class CaseStudy(models.Model):
# 1.15.2 # 1.15.2
image_caption = models.CharField( image_caption = models.CharField(
verbose_name=_("Image caption"), verbose_name=_("Image caption"),
max_length=500, max_length=240,
default=None, default=None,
null=True, null=True,
) )
@ -406,23 +445,25 @@ class CaseStudy(models.Model):
# 1.15.3 # 1.15.3
image_credit = models.CharField( image_credit = models.CharField(
verbose_name=_("Image credit(s)"), verbose_name=_("Image credit(s)"),
max_length=200, max_length=240,
default=None, default=None,
null=True, null=True,
) )
# 1.16.1 # 1.16.1
video = models.URLField( video = models.URLField(
verbose_name=_("YouTube Video"), verbose_name=_("Video URL"),
help_text=_("Copy the URL to a related YouTube™ video that relates to the case study."), help_text=_("Copy the URL to a YouTube or Vimeo video that relates to the case study."),
max_length=43, max_length=80,
validators=[validators.YoutubeURLValidator()] validators=[validators.YouTubeOrVimeoValidator()],
blank=True
) )
# 1.16.2 # 1.16.2
video_caption = models.CharField( video_caption = models.CharField(
verbose_name=_("Video caption"), verbose_name=_("Video caption"),
max_length=500, max_length=240,
blank=True,
default=None, default=None,
null=True, null=True,
) )
@ -430,15 +471,16 @@ class CaseStudy(models.Model):
# 1.16.3 # 1.16.3
video_credit = models.CharField( video_credit = models.CharField(
verbose_name=_("Video credit(s)"), verbose_name=_("Video credit(s)"),
max_length=500, max_length=240,
blank=True,
default=None, default=None,
null=True, null=True,
) )
# 1.17.1 # 1.17.1
media_coverage_mainstream = models.TextField( media_coverage_mainstream = models.TextField(
verbose_name=_("Links to media reports"), verbose_name=_("Links to media reports"),
help_text=_("Provide any links to mainstream media coverage."), help_text=_("Provide any links to mainstream media coverage."),
max_length=500,
default=None, default=None,
null=True, null=True,
) )
@ -447,7 +489,6 @@ class CaseStudy(models.Model):
media_coverage_independent = models.TextField( media_coverage_independent = models.TextField(
verbose_name=_("Independent grassroots reports"), verbose_name=_("Independent grassroots reports"),
help_text=_("Provide any links to grassroots/independent media coverage."), help_text=_("Provide any links to grassroots/independent media coverage."),
max_length=500,
default=None, default=None,
null=True, null=True,
) )
@ -456,7 +497,10 @@ class CaseStudy(models.Model):
community_voices = models.TextField( community_voices = models.TextField(
verbose_name=_("Community Voices"), verbose_name=_("Community Voices"),
help_text=_("Add any direct quotes from members of the community that \ help_text=_("Add any direct quotes from members of the community that \
relate to this project") relate to this project"),
default=None,
null=True,
blank=True
) )
# 1.18.2 # 1.18.2
@ -464,9 +508,9 @@ class CaseStudy(models.Model):
verbose_name=_("Reports of direct communications"), verbose_name=_("Reports of direct communications"),
help_text=_("Add any reports of direct communication between community members and \ help_text=_("Add any reports of direct communication between community members and \
representatives of developers/companies/investors."), representatives of developers/companies/investors."),
max_length=500,
default=None, default=None,
null=True, null=True,
blank=True
) )
# 1.18.3 # 1.18.3
@ -578,28 +622,33 @@ class CaseStudy(models.Model):
) )
# 2.2.2 # 2.2.2
energy_storage_capacity = models.IntegerField( energy_storage_capacity = models.DecimalField(
verbose_name=_("Energy storage capacity"), verbose_name=_("Energy storage capacity"),
help_text=_("Enter the total capacity of the energy storage system."), help_text=_("Enter the total capacity of the energy storage system in kilowatt-hours (kWh)."),
max_digits=20,
decimal_places=3,
default=None, default=None,
null=True, null=True,
blank=True blank=True
) )
# 2.2.2.1 # 2.2.2.1
maximum_power_output = models.BigIntegerField( maximum_power_output = models.DecimalField(
verbose_name=_('Maximum power output'), verbose_name=_('Maximum power output'),
help_text=_('Enter the maximum power output of the storage system in Watts (W). (W=J/s)'), help_text=_('Enter the maximum power output of the storage system in kilowatts (kW).'),
max_digits=12,
decimal_places=3,
default=None, default=None,
null=True, null=True,
blank=True blank=True
) )
# 2.2.2.2 # 2.2.2.2
discharge_time = models.BigIntegerField( discharge_time = models.DecimalField(
verbose_name=_('Time for discharge from full capacity'), verbose_name=_('Time for discharge from full capacity'),
help_text=_('Enter the time it takes to discharge from full capacity at maximum power output (in seconds) \ help_text=_('Enter the time it takes to discharge from full capacity at maximum power output (in hours).'),
(1h=3600s)'), max_digits=6,
decimal_places=3,
default=None, default=None,
null=True, null=True,
blank=True blank=True
@ -655,9 +704,9 @@ class CaseStudy(models.Model):
blank=True blank=True
) )
# 2.3.2.1 # 2.3.2
use_in_energy_economy = models.CharField( use_in_energy_economy = models.CharField(
verbose_name=_("Potential user in renewable energy economy"), verbose_name=_("Potential use in renewable energy economy"),
help_text=_("Select the potential use of the minerals in the renewable energy economy"), help_text=_("Select the potential use of the minerals in the renewable energy economy"),
max_length=3, max_length=3,
choices=USE_IN_ENERGY_ECONOMY_CHOICES, choices=USE_IN_ENERGY_ECONOMY_CHOICES,
@ -691,9 +740,9 @@ class CaseStudy(models.Model):
help_text=_("Describe the size of concession(s) granted to company/companies (e.g. 'one concession encompassing\ help_text=_("Describe the size of concession(s) granted to company/companies (e.g. 'one concession encompassing\
2,300 hectares')"), 2,300 hectares')"),
max_length=200, max_length=200,
default=None,
null=True, null=True,
blank=None default=None,
blank=True
) )
# 2.3.3.3 # 2.3.3.3
@ -704,13 +753,13 @@ class CaseStudy(models.Model):
max_length=256, max_length=256,
default=None, default=None,
null=True, null=True,
blank=None blank=True
) )
# 2.3.4 # 2.3.4
type_of_extraction = models.CharField( type_of_extraction = models.CharField(
verbose_name=_("Type of extraction"), verbose_name=_("Type of extraction"),
max_length=2, max_length=3,
choices=TYPE_OF_EXTRACTION_CHOICES, choices=TYPE_OF_EXTRACTION_CHOICES,
default=None, default=None,
null=True, null=True,
@ -795,19 +844,46 @@ class CaseStudy(models.Model):
) )
# 3.1.7.1 + 3.2.8.1 # 3.1.7.1 + 3.2.8.1
contact_email = models.EmailField(
verbose_name=_('Email address'),
blank=True
)
# 3.1.7.2 + 3.2.8.1 # 3.1.7.2 + 3.2.8.1
contact_phone = PhoneNumberField(
verbose_name=_('Phone number'),
help_text=_('Please include the international prefix, beginning with "+".'),
blank=True
)
# 3.1.7.3 + 3.2.8.1 # 3.1.7.3 + 3.2.8.1
contact_website = models.URLField(
verbose_name=_('Website'),
blank=True
)
# 3.1.7.4 + 3.2.8.1 # 3.1.7.4 + 3.2.8.1
contact_twitter = models.CharField(
verbose_name=_('Twitter username'),
max_length=50,
blank=True
)
# 3.1.7.5 + 3.2.8.1 # 3.1.7.5 + 3.2.8.1
contact_facebook = models.URLField(
verbose_name=_('Facebook page'),
blank=True
)
# 3.1.7.6 + 3.2.8.1 # 3.1.7.6 + 3.2.8.1
contact_other = models.TextField(
verbose_name=_('Other contact details'),
blank=True
)
# 3.2.1 # 3.2.1
negative_case_reasons = MultiSelectField( negative_case_reasons = MultiSelectField(
verbose_name=("Reasons this is a negative case study"),
choices=NEGATIVE_CASE_REASONS_CHOICES, choices=NEGATIVE_CASE_REASONS_CHOICES,
default=None, default=None,
null=True, null=True,
@ -890,13 +966,11 @@ class CaseStudy(models.Model):
) )
# 3.2.7 # 3.2.7
wants_conversation_with_ojuso = models.NullBooleanField( wants_conversation_with_ojuso = models.BooleanField(
verbose_name=_("Would you like to have a conversation with the ojuso team?"), verbose_name=_("Would you like to have a conversation with the ojuso team?"),
help_text=_("This would be a conversation about challenging or engaging related developers, companies and \ help_text=_("This would be a conversation about challenging or engaging related developers, companies and \
investors."), investors."),
default=None, default=True
null=True,
blank=True
) )
## ##
@ -952,23 +1026,20 @@ class CaseStudy(models.Model):
) )
# 4.4 # 4.4
shown_on_other_platforms = models.NullBooleanField( shown_on_other_platforms = models.BooleanField(
verbose_name=_("Show on other platforms?"), verbose_name=_("Is this case study shown on other platforms?"),
help_text=_("Tick the box if you would like us to show this case study on other social media platforms"), default=True
default=None,
null=True,
blank=True
) )
# 4.4.1 # 4.4.1
shown_on_other_platforms_detail = models.CharField( shown_on_other_platforms_detail = models.TextField(
verbose_name=_("Show on other platforms - Detail"), verbose_name=_("Shown on other platforms - Detail"),
help_text=_("List the social media platforms that you would like us to specifically publish the case study on"), help_text=_("Please provide links to other places the case study appears."),
max_length=128,
null=True,
blank=True blank=True
) )
objects = CaseStudyQuerySet.as_manager()
def __str__(self): def __str__(self):
"""The String representation of the case study. (Entry name with country name.)""" """The String representation of the case study. (Entry name with country name.)"""
return "%s in %s" % (self.entry_name, self.country.name) return "%s in %s" % (self.entry_name, self.country.name)
@ -986,9 +1057,49 @@ class CaseStudy(models.Model):
# Continue normal save method by calling original save method. # Continue normal save method by calling original save method.
super(CaseStudy, self).save(*args, **kwargs) super(CaseStudy, self).save(*args, **kwargs)
def get_video_id(self):
def is_video_youtube(self):
return self.video.count("youtube.com") > 0
def get_youtube_id(self):
"""Gets the 11 character YouTube video ID from the video field.""" """Gets the 11 character YouTube video ID from the video field."""
return parse.parse_qs(parse.urlparse(self.video).query)["v"][0] return parse.parse_qs(parse.urlparse(self.video).query)["v"][0]
def is_video_vimeo(self):
return self.video.count("vimeo.com") > 0
def get_vimeo_id(self):
"""Gets the 11 number video ID from the video field."""
return parse.urlparse(self.video).path
def get_negative_case_reasons_no_other(self):
"""Return a list of negative case reasons, minus the 'other' choice (if selected)"""
choices = self.get_negative_case_reasons_list()
if choices.count(self.NEGATIVE_CASE_REASONS_OTHER_TEXT) > 0:
choices.remove(self.NEGATIVE_CASE_REASONS_OTHER_TEXT)
return choices
def get_renewable_generation_detail(self):
"""Prepend appropriate descriptive text when accessing renewable generation type."""
if self.generation_technology:
if self.generation_technology.endswith('WE'):
return _('Wind energy') + " " + self.get_generation_technology_display()
elif self.generation_technology.endswith('PV'):
return _('Photovoltaic electricity') + " " + self.get_generation_technology_display()
elif self.generation_technology.endswith('HYD'):
return _('Hydroelectric') + " " + self.get_generation_technology_display()
elif self.generation_technology == 'OTHR':
return self.generation_technology_other
else:
return self.get_generation_technology_display()
else:
return ""
class Meta:
verbose_name_plural = 'case studies'

View File

@ -1,10 +0,0 @@
from moderation import moderation
from moderation.moderator import GenericModerator
from apps.map.models import CaseStudy
class CaseStudyModerator(GenericModerator):
notify_user = True
auto_approve_for_superusers = True
moderation.register(CaseStudy, CaseStudyModerator)

View File

@ -5,113 +5,573 @@
{% load leaflet_tags %} {% load leaflet_tags %}
{% load humanize %} {% load humanize %}
<!DOCTYPE html> {% block stylesheets %}
<html lang="en"> {{ super }}
<head> {% leaflet_css %}
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<script src="bundle.js"></script>
</head>
{% block content %}
<div class="container-fluid">
<div class="row" style="background-color: #e3f3fd;">
<div class="col-lg-8 border-top-0">
<button class="btn btn-info" style="margin:15px 0;padding-left:20px;left:-10px;position:absolute;" onclick="window.history.back()" role="button"><i class="fa fa-arrow-left" aria-hidden="true"></i> {% trans "Back to Map" %}</button>
<div style="margin-top:70px;padding:0 20px;">
<h1>{{case_study.entry_name}}</h1>
<p>{{case_study.synopsis}}</p>
</div>
<div class="clearfix">
<div style="margin-left:20px;">
<span class="label label-default">{{case_study.get_sector_of_economy_display}}</span>
{% if case_study.positive_or_negative == "P" %}
<span class="label label-success">{{case_study.get_positive_or_negative_display}}</span>
{% elif case_study.positive_or_negative == "N"%}
<span class="label label-danger">{{case_study.get_positive_or_negative_display}}</span>
{% endif %}
<span class="label label-info">{{case_study.country.name}}</span>
</br>
<small class="text-muted">Created {{case_study.date_created|naturaltime}} by <i>{{case_study.author}}</i></small>
</div>
<a class="btn btn-primary btn-lg float-right" href="#" role="button">Get Involved</a>
</div>
</div>
<div class="col-lg-4">
<div class="embed-responsive embed-responsive-16by9">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/{{case_study.get_video_id}}?rel=0&amp;showinfo=0" frameborder="0" allowfullscreen></iframe>
</div>
</div>
</div>
<div class="row" style="padding-top:30px;">
<div class="col-lg-8">
<div id="accordion" role="tablist">
<div class="card">
<div class="card-header" role="tab" id="headingOne">
<h5 class="mb-0">
<a data-toggle="collapse" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
{% trans "Full Description" %}
</a>
</h5>
</div>
<div id="collapseOne" class="collapse show" role="tabpanel" aria-labelledby="headingOne" data-parent="#accordion"> <style>
<div class="card-body"> body { font-size: 16px; }
{{case_study.full_description}}
</div> .col--header {
</div> background-color: #e3f3fd;
</div> padding: 0 40px;
<div class="card"> }
<div class="card-header" role="tab" id="headingTwo">
<h5 class="mb-0"> .btn--back { margin: 15px 0; padding-left: 20px; left: -10px; position: absolute; }
<a class="collapsed" data-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
{% trans "Community Voices" %} #case-location {
</a> height: 400px;
</h5> }
</div>
<div id="collapseTwo" class="collapse" role="tabpanel" aria-labelledby="headingTwo" data-parent="#accordion"> .case-mediacoverage a {
<div class="card-body"> display: block;
{{case_study.community_voices}} }
</div>
</div> .footer {
</div> background-color: #444;
</div> color: #ddd;
padding-top: 1em;
margin-top: 5em;
}
.linklist {
list-style-type: none;
text-align: right;
margin-left: 0;
padding-left: 0;
}
.linklist a {
color: #ddd;
text-decoration: underline;
}
.linklist a:hover {
color: #bbb;
}
.linklist-item--spacer {
margin-top: 1em;
}
dt { font-weight: bold; }
dd { margin-left: 2em; }
dd ul { padding-left: 0; margin-left: 0; }
</style>
{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2 col--header">
<button class="btn btn-info btn--back" onclick="window.history.back()" role="button"><i class="fa fa-arrow-left" aria-hidden="true"></i> {% trans "Back to Map" %}</button>
<div style="margin-top: 70px;">
<h1>{{ case_study.entry_name }}</h1>
</div>
<div class="clearfix">
<span class="label label-default">
{{ case_study.get_sector_of_economy_display }}
</span>&nbsp;
{% if case_study.positive_or_negative == "P" %}
<span class="label label-success">
{{ case_study.get_positive_or_negative_display }}
</span>&nbsp;
{% elif case_study.positive_or_negative == "N" %}
<span class="label label-danger">
{{ case_study.get_positive_or_negative_display }}
</span>&nbsp;
{% endif %}
<span class="label label-default">
{{ case_study.get_project_status_display }}
</span>&nbsp;
<span class="label label-info">
{{ case_study.country.name }}
</span>
</div>
<p class="text-muted"><small>Case study created {{case_study.date_created | naturaltime}}</small>
<p>{{ case_study.synopsis }}</p>
<figure style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
<img src="{{ case_study.image.url }}" width="100%">
<figcaption>
{% if case_study.image_caption %}
{{ case_study.image_caption }}<br>
{% endif %}
{% if case_study.image_credit %}
<i>{% trans "Credit(s)" %}: {{ case_study.image_credit }}</i>
{% endif %}
</figcaption>
</figure>
</div>
</div> <!-- end row -->
<div class="row" style="padding-top:30px;">
<div class="col-md-8 col-md-offset-2">
<h2>Contents</h2>
<p>...
<h2>Description</h2>
{{ case_study.full_description | linebreaks }}
<h2>{% trans "Affected land" %}</h2>
<dl>
{% if case_study.country %}
<dt>{% trans "Country" %}:
<dd>{{ case_study.get_country_display }}
{% endif %}
{% if case_study.location_context %}
<dt>{% trans "Rural or urban" %}:
<dd>{{ case_study.get_location_context_display }}
{% endif %}
{% if case_study.area_of_land %}
<dt>{% trans "Approximate land area" %}:
<dd>{{ case_study.area_of_land }} km²
{% endif %}
{% if case_study.land_ownership %}
<dt>{% trans "Land ownership" %}:
{% if case_study.land_ownership == 'OTH' %}
<dd>{{ case_study.land_ownership_details }}
{% else %}
<dd>{{ case_study.get_land_ownership_display }}
{% endif %}
{% endif %}
{% if case_study.type_of_ecosystem %}
<dt>{% trans "Type of ecosystem" %}:
<dd>{{ case_study.get_type_of_ecosystem_display }}
{% endif %}
{% if case_study.describe_ecosystem %}
<dt>{% trans "Description of ecosystem" %}:
<dd>{{ case_study.describe_ecosystem | linebreaks }}
{% endif %}
</dl>
{% if case_study.affects_indigenous %}
<h2>{% trans "Affected groups of people" %}</h2>
<h3>{% trans "Indigenous people" %}</h3>
{{ case_study.people_affected_indigenous | linebreaks }}
<h3>{% trans "Non-indigenous people" %}</h3>
{{ case_study.people_affected_other | linebreaks }}
{% endif %}
<h2>Project status</h2>
<dl>
{% if case_study.project_status %}
<dt>{% trans "Status" %}:
<dd id="project_status">{{ case_study.get_project_status_display }}
{% endif %}
{% if case_study.start_year %}
<dt>{% trans "Start year" %}:
<dd id="start_year">{{ case_study.start_year }}
{% endif %}
{% if case_study.completion_year %}
<dt>{% trans "Completion year" %}:
<dd id="completion_year">{{ case_study.completion_year }}
{% endif %}
{% if case_study.project_owners %}
<dt>{% trans "Project and facility owners" %}:
<dd id="project_owners">{{ case_study.project_owners }}
{% endif %}
{% if case_study.shareholders %}
<dt>{% trans "Shareholders of the project owners" %}:
<dd id="shareholders">{{ case_study.shareholders }}
{% endif %}
{% if case_study.financial_institutions %}
<dt>{% trans "Financial institutions" %}:
<dd id="financial_institutions">{{ case_study.financial_institutions }}
{% endif %}
{% if case_study.energy_customers %}
<dt>{% trans "Energy consumers" %}:
<dd id="energy_customers">{{ case_study.energy_customers }}
{% endif %}
</dl>
<h2>Multimedia</h2>
<figure>
<div class="embed-responsive embed-responsive-16by9">
{% if case_study.is_video_youtube %}
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/{{ case_study.get_youtube_id }}?rel=0&amp;showinfo=0" frameborder="0" allowfullscreen></iframe>
{% elif case_study.is_video_vimeo %}
<iframe src="https://player.vimeo.com/video/{{ case_study.get_vimeo_id }}" width="560" height="315" frameborder="0" allowfullscreen></iframe>
{% else %}
{{ case_study.video }}
{% endif %}
</div> </div>
<div class="col-lg-4">
<div class="card"> <figcaption>
<div class="card-header"> {% if case_study.video_caption %}
{% trans "Factbar" %} {{ case_study.video_caption }}<br>
</div> {% endif %}
<div class="card-body"> {% if case_study.video_credit %}
<ul class="list-group list-group-flush"> <i>{% trans "Credit(s)" %}: {{ case_study.video_credit }}</i>
{% if case_study.area_of_land %} {% endif %}
<li class="list-group-item">{% trans "Approximate land area"%}: {{case_study.area_of_land}} km²</li> </figcaption>
{% endif %} </figure>
</ul>
</div> {% if case_study.media_coverage_mainstream or case_study.media_coverage_independent or case_study.social_media_links %}
</div> <div class="case-mediacoverage">
<div class="card"> <h2>{% trans "Media coverage" %}</h2>
<div class="card-header">
Attached Files {% if case_study.media_coverage_mainstream %}
</div> <h3>{% trans "Mainstream media" %}</h3>
<ul class="list-group list-group-flush"> {{ case_study.media_coverage_mainstream | urlize }}
<li class="list-group-item"> {% endif %}
<a href="#">File One</a></br>
<small class="text-muted">Uploaded one month ago</small> {% if case_study.media_coverage_independent %}
</li> <h3>{% trans "Independent/grassroots media" %}</h3>
<li class="list-group-item"> {{ case_study.media_coverage_independent | urlize }}
<a href="#">File Two</a></br> {% endif %}
<small class="text-muted">Uploaded 3 mins ago</small>
</li> {% if case_study.social_media_links %}
<h3>{% trans "Social media" %}</h3>
{{ case_study.social_media_links | urlize }}
{% endif %}
</div>
{% endif %}
<h2>{% trans "Community Voices" %}</h2>
{{ case_study.community_voices | linebreaks }}
{% if case_study.direct_comms %}
<h2>{% trans "Reports of direct communications" %}</h2>
{{ case_study.direct_comms | linebreaks }}
{% endif %}
<h1>Technical and economic analysis</h1>
{% if case_study.sector_of_economy == 'RN' %}
<!-- renewables / 2.1 -->
<dl>
{% if case_study.generation_technology %}
<dt>{% trans "Generation technology" %}:
<dd id="generation_technology">
{{ case_study.get_renewable_generation_detail }}
{% endif %}
{% if case_study.generation_technology == 'BIOG' or case_study.generation_technology == 'OTHB' %}
<dt>{% trans "Feedstock" %}:
<dd id="biomass_detail">
{{ case_study.biomass_detail }}
{% endif %}
{% if case_study.total_generation_capacity %}
<dt>{% trans "Total generation capacity" %}:
<dd id="total_generation_capacity">
{{ case_study.total_generation_capacity }} kW
{% endif %}
{% if case_study.generation_equipment_supplier %}
<dt>{% trans "Generation equipment supplier" %}:
<dd id="generation_equipment_supplier">
{{ case_study.generation_equipment_supplier }}
{% endif %}
{% if case_study.total_investment %}
<dt>{% trans "Approximate total investment" %}:
<dd id="total_investment">
${{ case_study.total_investment | intcomma }} (USD)
{% endif %}
{% if case_study.technical_or_economic_details %}
<dt>{% trans "Additional technical or economic details" %}:
<dd id="technical_or_economic_details">
{{ case_study.technical_or_economic_details | linebreaks }}
{% endif %}
</dl>
{% elif case_study.sector_of_economy == 'PG' %}
<!-- batteries or storage / 2.2 -->
<dl>
{% if case_study.power_technology %}
<dt>{% trans "Generation technology" %}:
<dd id="power_technology">
{% if case_study.power_technology == 'OT' %}
{{ case_study.power_technology_other }}
{% else %}
{{ case_study.power_technology }}
{% endif %}
{% endif %}
{% if case_study.energy_storage_capacity %}
<dt>{% trans "Energy storage capacity" %}:
<dd id="energy_storage_capacity">
{{ case_study.energy_storage_capacity }} TODO UNITS?
{% endif %}
{% if case_study.maximum_power_output %}
<dt>{% trans "Maximum power output" %}:
<dd id="maximum_power_output">
{{ case_study.maximum_power_output | intcomma }} W
{% endif %}
{% if case_study.discharge_time %}
<dt>{% trans "Time for discharge from full capacity" %}:
<dd id="discharge_time">
{{ case_study.discharge_time | intcomma }} {% trans "seconds" %}
{% endif %}
{% if case_study.contractor_or_supplier_of_technology %}
<dt>{% trans "Contractor and/or supplier of technology" %}:
<dd id="contractor_or_supplier_of_technology">
{{ case_study.contractor_or_supplier_of_technology }}
{% endif %}
{% if case_study.approximate_total_investment %}
<dt>{% trans "Approximate total investment" %}:
<dd id="approximate_total_investment">
${{ case_study.approximate_total_investment | intcomma }} (USD)
{% endif %}
{% if case_study.additional_technical_details %}
<dt>{% trans "Additional technical or economic details" %}:
<dd id="additional_technical_details">
{{ case_study.additional_technical_details | linebreaks }}
{% endif %}
</dl>
{% elif case_study.sector_of_economy == 'SM' %}
<!-- minerals / 2.3 -->
<dl>
{% if case_study.minerals_or_commodities %}
<dt>{% trans "Mineral commodity/commodities" %}:
<dd id="minerals_or_commodities">
{% if case_study.minerals_or_commodities == 'OTR' %}
{{ case_study.minerals_or_commodities_other }}
{% else %}
{{ case_study.get_minerals_or_commodities_display }}
{% endif %}
{% endif %}
{% if case_study.use_in_energy_economy %}
<dt>{% trans "Potential use in renewable energy economy" %}:
<dd id="use_in_energy_economy">
{% if case_study.use_in_energy_economy == 'OTR' %}
{{ case_study.use_in_energy_economy_other }}
{% else %}
{{ case_study.get_use_in_energy_economy_display }}
{% endif %}
{% endif %}
{% if case_study.project_life_span %}
<dt>{% trans "Project life span" %}:
<dd id="project_life_span">
{{ case_study.project_life_span }}
{% endif %}
{% if case_study.size_of_concessions %}
<dt>{% trans "Size of concessions" %}:
<dd id="size_of_concessions">
{{ case_study.size_of_concessions }}
{% endif %}
{% if case_study.projected_production_of_commodities %}
<dt>{% trans "Projected production of key commodities" %}:
<dd id="projected_production_of_commodities">
{{ case_study.projected_production_of_commodities }}
{% endif %}
{% if case_study.type_of_extraction %}
<dt>{% trans "Type of extraction" %}:
<dd id="type_of_extraction">
{{ case_study.get_type_of_extraction_display }}
{% endif %}
{% if case_study.associated_infrastructure %}
<dt>{% trans "Type of extraction" %}:
<dd id="associated_infrastructure">
{{ case_study.associated_infrastructure }}
{% endif %}
</dl>
{% endif %}
<h2>Socio-economic analysis</h2>
<dl>
<dt>{% trans "Case type" %}
<dd>{{ case_study.get_positive_or_negative_display }}
{% if case_study.positive_or_negative == "P" %}
<!-- positive case -->
{% if case_study.positive_case_type %}
<dt>{% trans "Type of positive case" %}:
<dd id="positive_case_type">
{{ case_study.get_positive_case_type_display }}
{% endif %}
{% if case_study.socioeconomic_benefits %}
<dt>{% trans "Socio-economic benefits" %}:
<dd id="socioeconomic_benefits">
{{ case_study.socioeconomic_benefits }}
{% endif %}
{% if case_study.key_actors_involved %}
<dt>{% trans "Key actors involved" %}:
<dd id="key_actors_involved">
{{ case_study.key_actors_involved }}
{% endif %}
{% if case_study.project_status_detail %}
<dt>{% trans "Current status of project" %}:
<dd id="project_status_detail">
{{ case_study.project_status_detail }}
{% endif %}
{% if case_study.obstacles_and_hindrances %}
<dt>{% trans "Obstacles and hindrances" %}:
<dd id="obstacles_and_hindrances">
{{ case_study.obstacles_and_hindrances }}
{% endif %}
{% if case_study.identified_partnerships %}
<dt>{% trans "Identified partnerships" %}:
<dd id="identified_partnerships">
{{ case_study.identified_partnerships }}
{% endif %}
{% else %}
<!-- negative case -->
{% if case_study.negative_case_reasons %}
<dt>{% trans "Reasons this is a negative case study" %}:
<dd id="negative_case_reasons">
<ul>
{% for text in case_study.get_negative_case_reasons_no_other %}
<li>{{ text }}
{% endfor %}
{% if case_study.negative_case_reasons_other %}
<li>{{ case_study.negative_case_reasons_other }}
{% endif %}
</ul> </ul>
</div> {% endif %}
</div>
{% if case_study.negative_socioenvironmental_impacts %}
<dt>{% trans "Negative socio-environmental impacts" %}:
<dd id="negative_socioenvironmental_impacts">
{{ case_study.negative_socioenvironmental_impacts }}
{% endif %}
{% if case_study.isolated_or_widespread %}
<dt>{% trans "Isolated or commonplace" %}:
<dd id="isolated_or_widespread">
{{ case_study.isolated_or_widespread }}
{% endif %}
{% if case_study.when_did_organising_start %}
<dt>{% trans "Local organising efforts began" %}:
<dd id="when_did_organising_start">
{{ case_study.when_did_organising_start }}
{% endif %}
{% if case_study.who_has_been_involved %}
<dt>{% trans "Communities, groups and organisations involved" %}:
<dd id="who_has_been_involved">
{{ case_study.who_has_been_involved }}
{% endif %}
{% if case_study.potential_partnerships %}
<dt>{% trans "Potential partnerships" %}:
<dd id="potential_partnerships">
{{ case_study.potential_partnerships }}
{% endif %}
{% endif %}
</dl>
<h2>Related documents</h2>
{% if case_study.official_project_documents %}
<p>Official documents: {{ case_study.official_project_documents.url }}
{% endif %}
{% if case_study.other_documents %}
<p>Other documents: {{ case_study.other_documents.url }}
{% endif %}
<h2>Location / GIS data</h2>
{% leaflet_map "case_location" callback="window.map_init" %}
{% if case_study.shapefiles %}
<p>Shapefiles: {{ case_study.shapefiles.url }}
{% endif %}
{% if case_study.coordinate_reference_system %}
<p>Coordinate reference system: {{ coordinate_reference_system }}
{% endif %}
{% if case_study.name_of_territory_or_area %}
<p>Name of territory or area: {{ case_study.name_of_territory_or_area }}
{% endif %}
</div>
</div>
</div>
<footer class="footer">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<ul class="linklist">
<li><a href="{% url 'index' %}">Map of all case studies</a>
<li><a href="/about">About us</a>
<li class="linklist-item--spacer">&copy; Ojuso 2018
</ul>
</div> </div>
</div> </div>
<footer class="footer text-center"> </div>
<div class="container-fluid"> </footer>
<span class="text-muted">Ojuso x Yansa</span>
</div> {% endblock %}
</footer>
{% block scripts %}
{% leaflet_js %}
<script>
function map_init(map, options) {
var latlng = L.latLng(
{{ case_study.location.coords | last }},
{{ case_study.location.coords | first }}
);
map.setView(latlng, 6);
L.marker(latlng).addTo(map);
}
</script>
{% endblock %} {% endblock %}
</html>

View File

@ -64,7 +64,7 @@
{ // 2.1 - Power Generation { // 2.1 - Power Generation
"field": "#id_sector_of_economy", "field": "#id_sector_of_economy",
"showHide": ["#power_generation_questions"], "showHide": ["#power_generation_questions"],
"condition": ["WND","SOL","HYD"] "condition": ["RN"]
}, },
{ {
"field": "#id_generation_technology", "field": "#id_generation_technology",
@ -81,7 +81,7 @@
"showHide": ["#power_grids_energy_storage_questions"], "showHide": ["#power_grids_energy_storage_questions"],
"condition": ["PG"] "condition": ["PG"]
}, },
{ { // 2.3 - Supply of minerals
"field": "#id_sector_of_economy", "field": "#id_sector_of_economy",
"showHide": ["#mineral_commodity_questions"], "showHide": ["#mineral_commodity_questions"],
"condition": ["SM"] "condition": ["SM"]
@ -103,10 +103,16 @@
// Here we define the checkboxes that we need to use to // Here we define the checkboxes that we need to use to
// conditionally toggle fields - they use slightly different // conditionally toggle fields - they use slightly different
// logic as they rely on the 'checked' attribute rather than value. // logic as they rely on the 'checked' attribute rather than value.
var conditionalCheckboxes = [{ var conditionalCheckboxes = [
"checkbox": "#id_affects_indigenous", {
"showHide": "#div_id_affects_indigenous_detail" "checkbox": "#id_affects_indigenous",
}]; "showHide": "#div_id_affects_indigenous_detail"
},
{
"checkbox": "#id_shown_on_other_platforms",
"showHide": "#div_id_shown_on_other_platforms_detail"
}
];
// Define a function that hides the field and then creates a listener to toggle the field. // Define a function that hides the field and then creates a listener to toggle the field.
// Takes a single conditionalField dictionary with (field, showHide and condition). // Takes a single conditionalField dictionary with (field, showHide and condition).

View File

@ -5,116 +5,159 @@
{% load static %} {% load static %}
{% block stylesheets %} {% block stylesheets %}
{{ block.super }} {{ block.super }}
{% leaflet_css %} {% leaflet_css %}
<style> <style>
html, body, #main { html, body, #main {
width: 100; height:100%; width: 100;
} height: 100%;
.leaflet-popup-content > a{ }
color: #fff;
}
</style>
{% endblock %}
.leaflet-popup-content > a {
color: #fff;
}
.popup-head h5 {
margin-bottom: 0;
}
.popup-head p {
margin-top: 0;
}
.popup-labels {
margin: 10px auto 15px;
}
.popup-labels .label {
font-size: 100%;
}
</style>
{% endblock %}
{% block title %}{% trans "Ojuso Platform Map" %}{% endblock %} {% block title %}{% trans "Ojuso Platform Map" %}{% endblock %}
{% block inner_content %} {% block inner_content %}
<div id="main"></div> <div id="main"></div>
<div id="modals"></div> <div id="modals"></div>
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
{% leaflet_map "main" callback="main_app_init" creatediv=False %} {% leaflet_map "main" callback="main_app_init" creatediv=False %}
{% leaflet_js %} {% leaflet_js %}
<script type="text/javascript" src="{% static 'map/plugins/leaflet-hash.js' %}"></script> <script type="text/javascript" src="{% static 'map/plugins/leaflet-hash.js' %}"></script>
<script type="text/javascript"> <script>
// This takes HTML as a string and returns an element function getLabelClass(pos_or_neg) {
function create(htmlStr) { if (pos_or_neg == "N") {
var frag = document.createDocumentFragment(), return "danger";
temp = document.createElement('div'); } else {
temp.innerHTML = htmlStr; return "success";
while (temp.firstChild) { }
frag.appendChild(temp.firstChild); }
}
return frag; function popup(feature, layer) {
} layer.bindPopup(
"<img src='"+feature.properties.image+"' width='100%'>"+
// This is called when the map is initialized "<div class='popup-head'>"+
function main_app_init(map, options) { "<h5>"+feature.properties.entry_name+"</h5>" +
var hash = new L.hash(map); "<i>"+feature.properties.country_name+"</i>"+
// Pull data as GeoJSON and add to map with a modal "</div>"+
$.getJSON('/api/case-studies/', function (data) { "<div class='popup-labels'>"+
L.geoJson(data, { "<span class='label label-default'>"+feature.properties.sector_of_economy+"</span> "+
onEachFeature: function (feature, layer) { "<span class='label label-"+getLabelClass(feature.properties.positive_or_negative)+"'>"+ feature.properties.positive_or_negative_display+"</span>"+
var modalname = "case-study-"+feature.id "</div>"+
layer.bindPopup( "<a class='btn btn-sm btn-primary' href='case-study/"+feature.properties.slug+"'>{% trans "View full case study" %}</a>"
"<p>"+feature.properties.entry_name+"</p>"+ );
"<a class='btn btn-primary' href='case-study/"+feature.properties.slug+"' role='button'>" };
+"{% trans "View" %}"+"</a>"
); // This is called when the map is initialized
var modal = create( function main_app_init(map, options) {
"<div class='modal fade' id='"+modalname+"' tabindex='-1' role='dialog' aria-labelledby='"+modalname+"-label'>"+ var hash = new L.hash(map);
"<div class='modal-dialog' role='document'>"+
"<div class='modal-content'>"+ // Pull data as GeoJSON and add to map with a modal
"<div class='modal-header'>"+ $.getJSON('/api/case-studies/', function(data) {
"<button type='button' class='close' data-dismiss='modal' aria-label='{% trans "Close" %}'>"+ L.geoJson(data, {
"<span aria-hidden='true'>&times;</span>"+ onEachFeature: popup
"</button>"+ }).addTo(map)
"<h4 class='modal-title' id='"+modalname+"-label'>"+ });
feature.properties.project_name+ }
"</h4>"+
"</div>"+ </script>
"<div class='modal-body'>"+
"<p>"+feature.properties.description+"</p>"+
"<dl class='dl-horizontal'>"+
"<dt>Supply Chain</dt><dd>"+feature.properties.supply_chain+"</dd>"+
"<dt>Generation Type</dt><dd>"+feature.properties.generation_type+"</dd>"+
"<dt>Associated Companies</dt><dd>"+feature.properties.associated_companies+"</dd>"+
"<dt>Financiers</dt><dd>"+feature.properties.financiers+"</dd>"+
"<dt>Important Lenders</dt><dd>"+feature.properties.important_lenders+"</dd>"+
"<dt>Country</dt><dd>"+feature.properties.country+"</dd>"+
"<dt>Affects Indigenous</dt><dd>"+feature.properties.affects_indigenous+"</dd>"+
"<dt>Affects Indigenous Reason</dt><dd>"+feature.properties.affects_indigenous_reason+"</dd>"+
"<dt>Proposed Start</dt><dd>"+feature.properties.proposed_start+"</dd>"+
"<dt>Proposed Completion</dt><dd>"+feature.properties.proposed_completion+"</dd>"+
"<dt>Link to Forum</dt><dd>"+feature.properties.link_to_forum+"</dd>"+
"<dt>Image</dt><dd>"+feature.properties.image+"</dd>"+
"<dt>References</dt><dd>"+feature.properties.references+"</dd>"+
"<dt>Commodities</dt><dd>"+feature.properties.commodities+"</dd>"+
"<dt>Like to Engage Developer</dt><dd>"+feature.properties.like_to_engage_developer+"</dd>"+
"<dt>Like to Engage Investors</dt><dd"+feature.properties.like_to_engage_investors+"></dd>"+
"<dt>Author</dt><dd>"+feature.properties.author+"</dd>"+
"</dl>"+
"</div>"+
"<div class='modal-footer'>"+
"<button type='button' class='btn btn-default' data-dismiss='modal'>"+
"{% trans "Close" %}"+
"</button>"+
"<button type='button' class='btn btn-primary'>"+
"{% trans "Save changes" %}"+
"</button>"+
"</div>"+
"</div>"+
"</div>"+
"</div>"
);
document.getElementById('modals').appendChild(modal);
}
}).addTo(map);
});
<!--// Add an on-click listener for map click events. Show popup with button to submit a casestudy-->
<!--map.on('click', function(e) {-->
<!--var popup = L.popup()-->
<!--.setLatLng(e.latlng)-->
<!--.setContent("<a class='btn btn-primary btn-sm' href='{% url 'create' %}?lat="+e.latlng.lat+"&lng="+e.latlng.lng+"' role='button'>{% trans "Submit a Case Study" %}</a>")-->
<!--.openOn(map);-->
<!--});-->
}
</script>
{% endblock %} {% endblock %}
<script type="text/comment">
// removed from <script> section above
// This takes HTML as a string and returns an element
function create(htmlStr) {
var frag = document.createDocumentFragment();
var temp = document.createElement('div');
temp.innerHTML = htmlStr;
while (temp.firstChild) {
frag.appendChild(temp.firstChild);
}
return frag;
}
var modalname = "case-study-" + feature.id
var modal = create(
"<div class='modal fade' id='"+modalname+"' tabindex='-1' role='dialog' aria-labelledby='"+modalname+"-label'>"+
"<div class='modal-dialog' role='document'>"+
"<div class='modal-content'>"+
"<div class='modal-header'>"+
"<button type='button' class='close' data-dismiss='modal' aria-label='{% trans "Close" %}'>"+
"<span aria-hidden='true'>&times;</span>"+
"</button>"+
"<h4 class='modal-title' id='"+modalname+"-label'>"+
feature.properties.project_name+
"</h4>"+
"</div>"+
"<div class='modal-body'>"+
"<p>"+feature.properties.description+"</p>"+
"<dl class='dl-horizontal'>"+
"<dt>Supply Chain</dt><dd>"+feature.properties.supply_chain+"</dd>"+
"<dt>Generation Type</dt><dd>"+feature.properties.generation_type+"</dd>"+
"<dt>Associated Companies</dt><dd>"+feature.properties.associated_companies+"</dd>"+
"<dt>Financiers</dt><dd>"+feature.properties.financiers+"</dd>"+
"<dt>Important Lenders</dt><dd>"+feature.properties.important_lenders+"</dd>"+
"<dt>Country</dt><dd>"+feature.properties.country+"</dd>"+
"<dt>Affects Indigenous</dt><dd>"+feature.properties.affects_indigenous+"</dd>"+
"<dt>Affects Indigenous Reason</dt><dd>"+feature.properties.affects_indigenous_reason+"</dd>"+
"<dt>Proposed Start</dt><dd>"+feature.properties.proposed_start+"</dd>"+
"<dt>Proposed Completion</dt><dd>"+feature.properties.proposed_completion+"</dd>"+
"<dt>Link to Forum</dt><dd>"+feature.properties.link_to_forum+"</dd>"+
"<dt>Image</dt><dd>"+feature.properties.image+"</dd>"+
"<dt>References</dt><dd>"+feature.properties.references+"</dd>"+
"<dt>Commodities</dt><dd>"+feature.properties.commodities+"</dd>"+
"<dt>Like to Engage Developer</dt><dd>"+feature.properties.like_to_engage_developer+"</dd>"+
"<dt>Like to Engage Investors</dt><dd"+feature.properties.like_to_engage_investors+"></dd>"+
"<dt>Author</dt><dd>"+feature.properties.author+"</dd>"+
"</dl>"+
"</div>"+
"<div class='modal-footer'>"+
"<button type='button' class='btn btn-default' data-dismiss='modal'>"+
"{% trans "Close" %}"+
"</button>"+
"<button type='button' class='btn btn-primary'>"+
"{% trans "Save changes" %}"+
"</button>"+
"</div>"+
"</div>"+
"</div>"+
"</div>"
);
document.getElementById('modals').appendChild(modal);
// Add an on-click listener for map click events. Show popup with button to submit a casestudy
// map.on('click', function(e) {
// var popup = L.popup()
// .setLatLng(e.latlng)
// .setContent("<a class='btn btn-primary btn-sm' href='{% url 'create' %}?lat="+e.latlng.lat+"&lng="+e.latlng.lng+"' role='button'>{% trans "Submit a Case Study" %}</a>")
// .openOn(map);
// });
</script>

View File

@ -1,3 +1,49 @@
from django.test import TestCase from django.test import TestCase
# Create your tests here. from .models import CaseStudy
class CaseStudyTests(TestCase):
def test_get_renewable_generation_detail_when_empty(self):
"""get_renewable_generation_detail() should return the empty string."""
case_study = CaseStudy()
self.assertIs(case_study.get_renewable_generation_detail(), "")
def test_get_renewable_generation_detail_with_geo(self):
"""get_renewable_generation_detail() should return just the description"""
case_study = CaseStudy(generation_technology='GEOT')
self.assertEqual(case_study.get_renewable_generation_detail(), "Geothermal electricity")
def test_get_renewable_generation_detail_with_wind(self):
"""get_renewable_generation_detail() should return the description prefixed with 'wind power'"""
case_study = CaseStudy(generation_technology='SSWE')
self.assertEqual(case_study.get_renewable_generation_detail(), "Wind energy Small-scale (less than 500kW)")
def test_get_renewable_generation_detail_with_other(self):
"""get_renewable_generation_detail() should return the detail provided in .generation_technology_other"""
case_study = CaseStudy(generation_technology='OTHR', generation_technology_other='Warp drive')
self.assertEqual(case_study.get_renewable_generation_detail(), "Warp drive")
# These tests are commented out because they are not working, but the code
# in production is. When running as a test, get_negative_case_reasons_no_other()
# is returning a list of coded options, like
# ['V', 'O', 'L', 'R', ',', 'A', 'L', 'A', 'B']
# instead of a list of text like
# ['Violation of land rights', 'Abusive labour practices']
#  I am too much of a Django newbie to know why.  Anna
# def test_get_negative_case_reasons_no_other_1(self):
# """Test with case having no 'other' entry"""
# case_study = CaseStudy(negative_case_reasons='VOLR,ALAB')
# self.assertEqual(case_study.get_negative_case_reasons_no_other(),
# [ 'Violation of land rights'
# , 'Abusive labour practices'
# ])
#
# def test_get_negative_case_reasons_no_other_2(self):
# """Test with case having an 'other' entry"""
# case_study = CaseStudy(negative_case_reasons='VOLR,ALAB,OTHR')
# self.assertEqual(case_study.get_negative_case_reasons_no_other(),
# [ 'Violation of land rights'
# , 'Abusive labour practices'
# ])

View File

@ -1,5 +1,19 @@
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
# Supported formats:
# - http://(www.)youtube.com/watch?v=Dhjiu89G3
# - http://(www.)youtube.com/watch/Dhjiu89G3
# - http://youtu.be/Dhjiu89G3
class YoutubeURLValidator(RegexValidator): class YoutubeURLValidator(RegexValidator):
regex = r'https?:\/\/(((www.)?youtube.com\/((watch\?v=)|(watch\/)))|(youtu.be\/))([A-z0-9]{1,11}).+' regex = r'https?:\/\/(((www.)?youtube.com\/((watch\?v=)|(watch\/)))|(youtu.be\/))([A-z0-9]{1,11}).+'
# Supported Vimeo formats:
# - http://(www.)vimeo.com/258651879
# - http://player.vimeo.com/video/258651879
class VimeoURLValidator(RegexValidator):
regex = r'https?:\/\/(player|www.)?vimeo.com\/([0-9]{1,11}).+'
class YouTubeOrVimeoValidator(RegexValidator):
regex = r'https?:\/\/(player.|www.)?(vimeo\.com|youtu(be\.com|\.be))\/(video\/|embed\/|watch\?v=|v\/)?([A-Za-z0-9]{1,11}).+'

View File

@ -1,3 +1,5 @@
from django.core.mail import send_mail
from django.conf import settings
from django.views.generic import DetailView from django.views.generic import DetailView
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.views.generic.edit import CreateView from django.views.generic.edit import CreateView
@ -6,6 +8,17 @@ from django.urls import reverse
from .models import CaseStudy from .models import CaseStudy
from .forms import ShortCaseStudyForm, LongCaseStudyForm from .forms import ShortCaseStudyForm, LongCaseStudyForm
NOTIFY_MESSAGE = """
Hello,
Someone has submitted a new case study to the Ojuso website. Please
follow the below link to look over and approve it:
%s%s
Case Study Robot
"""
class Map(TemplateView): class Map(TemplateView):
template_name = "map/index.html" template_name = "map/index.html"
@ -18,9 +31,28 @@ class Create(LoginRequiredMixin, TemplateView):
class BaseForm(LoginRequiredMixin, CreateView): class BaseForm(LoginRequiredMixin, CreateView):
"""View for base case study form.""" """View for base case study form."""
template_name = 'map/form.html' template_name = 'map/form.html'
success_url = '/' success_url = '/case-study/create/success/'
model = CaseStudy model = CaseStudy
def send_email(self):
"""Sends email to moderator to approve case study."""
send_mail(
'New case study submitted',
NOTIFY_MESSAGE % (
settings.SITE_URL,
reverse('admin:map_casestudy_change', args=[self.object.id])
),
'noreply@ojuso.org',
['database@ojuso.org'],
fail_silently=False,
)
def form_valid(self, form):
self.object = form.save()
self.send_email()
return super().form_valid(form)
class ShortForm(BaseForm): class ShortForm(BaseForm):
"""View for short version of case study form.""" """View for short version of case study form."""

View File

@ -22,7 +22,7 @@ services:
env_file: env_file:
- ./environment - ./environment
command: /bin/sh -c "DEBUG=1 python3 manage.py collectstatic --noinput && python3 manage.py compilemessages && python3 manage.py migrate && gunicorn --bind 0.0.0.0:8000 ojusomap.wsgi" command: /bin/sh -c "DEBUG=1 python3 manage.py collectstatic --noinput && python3 manage.py compilemessages && python3 manage.py migrate && gunicorn --bind 0.0.0.0:8000 ojusomap.wsgi"
db: db:
restart: always restart: always
image: mdillon/postgis:9.6-alpine image: mdillon/postgis:9.6-alpine
@ -35,7 +35,7 @@ services:
- "127.0.0.1:5432:5432" - "127.0.0.1:5432:5432"
env_file: env_file:
- ./environment - ./environment
weblate: weblate:
image: weblate/weblate image: weblate/weblate
links: links:
@ -50,14 +50,13 @@ services:
- "traefik.frontend.passHostHeader=true" - "traefik.frontend.passHostHeader=true"
volumes: volumes:
- weblate-data:/app/data - weblate-data:/app/data
env_file: env_file:
- ./environment - ./environment
environment: environment:
- "POSTGRES_USER=weblate" - "POSTGRES_USER=weblate"
- "POSTGRES_DATABASE=weblate" - "POSTGRES_DATABASE=weblate"
restart: always restart: always
cache: cache:
image: memcached:1.4 image: memcached:1.4
restart: always restart: always
@ -78,7 +77,6 @@ services:
- /containers/jenkins:/var/jenkins_home - /containers/jenkins:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
weblate_static: weblate_static:
image: nginx:alpine image: nginx:alpine
restart: always restart: always

View File

@ -66,8 +66,8 @@ WEBLATE_GITHUB_USERNAME=livmackintosh
#WEBLATE_REQUIRE_LOGIN=1 #WEBLATE_REQUIRE_LOGIN=1
# Mail server, the server has to listen on port 587 and understand TLS # Mail server, the server has to listen on port 587 and understand TLS
WEBLATE_EMAIL_HOST=mail.gandi.net WEBLATE_EMAIL_HOST=smtp.mailgun.org
# Do NOT use quotes here # Do NOT use quotes here
WEBLATE_EMAIL_USER=admin@ojuso.org WEBLATE_EMAIL_USER=postmaster@mail.ojuso.org
# Do NOT use quotes here # Do NOT use quotes here
WEBLATE_EMAIL_PASSWORD=${SMTP_PASSWORD} WEBLATE_EMAIL_PASSWORD=${SMTP_PASSWORD}

View File

@ -4,7 +4,6 @@ services:
build: . build: .
links: links:
- db:db - db:db
- cache:cache
- mailhog:mail - mailhog:mail
volumes: volumes:
- .containers/map/static:/app/static - .containers/map/static:/app/static
@ -16,15 +15,15 @@ services:
- 8000:8000 - 8000:8000
env_file: env_file:
- ./local.env - ./local.env
command: /bin/sh -c "python3 manage.py collectstatic --noinput ; python3 manage.py runserver 0.0.0.0:8000" command: /bin/sh -c "python3 manage.py runserver 0.0.0.0:8000"
db: db:
image: mdillon/postgis:9.6-alpine image: mdillon/postgis:9.6-alpine
volumes: volumes:
- .containers/db:/var/lib/postgresql/data - postgres-data-volume:/var/lib/postgresql/data
- ./support/postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d - ./support/postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
ports: ports:
- "127.0.0.1:5432:5432" - 5432:5432
env_file: env_file:
- ./local.env - ./local.env
@ -33,9 +32,7 @@ services:
ports: ports:
- 1025:1025 - 1025:1025
- 8025:8025 - 8025:8025
cache:
image: memcached:1.4
volumes: volumes:
weblate-data: weblate-data:
postgres-data-volume:

View File

@ -42,31 +42,31 @@ INSTALLED_APPS = [
'apps.contact', 'apps.contact',
'apps.map', 'apps.map',
'apps.profiles', 'apps.profiles',
'django.contrib.admin',
'registration',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.flatpages',
'django.contrib.humanize',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'whitenoise.runserver_nostatic',
'django.contrib.staticfiles',
'django.contrib.gis',
'avatar', 'avatar',
'bootstrap3', 'bootstrap3',
'cas_server', 'cas_server',
'compressor', 'compressor',
'crispy_forms', 'crispy_forms',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.flatpages',
'django.contrib.gis',
'django.contrib.humanize',
'django.contrib.messages',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.staticfiles',
'django_extensions', 'django_extensions',
'envelope', 'envelope',
'leaflet', 'leaflet',
'moderation',
'raven.contrib.django.raven_compat', 'raven.contrib.django.raven_compat',
'registration',
'rest_framework', 'rest_framework',
'rest_framework_gis', 'rest_framework_gis',
'storages' 'storages',
'whitenoise.runserver_nostatic',
'anymail',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -85,6 +85,7 @@ MIDDLEWARE = [
ROOT_URLCONF = 'ojusomap.urls' ROOT_URLCONF = 'ojusomap.urls'
SITE_ID = 1 SITE_ID = 1
SITE_URL = 'https://map.ojuso.org'
TEMPLATES = [ TEMPLATES = [
{ {
@ -107,7 +108,7 @@ WSGI_APPLICATION = 'ojusomap.wsgi.application'
# E-Mail # E-Mail
# https://docs.djangoproject.com/en/1.11/topics/email/ # https://docs.djangoproject.com/en/1.11/topics/email/
ADMINS = [('Livvy','livvy@base.nu')] ADMINS = [('Autonomic','autonomic-coop@posteo.net')]
DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'webmaster@localhost') DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'webmaster@localhost')
EMAIL_HOST = os.getenv('EMAIL_HOST', 'localhost') EMAIL_HOST = os.getenv('EMAIL_HOST', 'localhost')
EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD') EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD')
@ -117,6 +118,12 @@ EMAIL_USE_TLS = bool(int(os.getenv('EMAIL_USE_TLS', False)))
EMAIL_SUBJECT_PREFIX = "Ojuso Platform" EMAIL_SUBJECT_PREFIX = "Ojuso Platform"
SERVER_EMAIL = os.getenv('SERVER_EMAIL', 'root@localhost') SERVER_EMAIL = os.getenv('SERVER_EMAIL', 'root@localhost')
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
ANYMAIL = {
"MAILGUN_API_KEY": os.getenv("MAILGUN_API_KEY", ""),
"MAILGUN_SENDER_DOMAIN": os.getenv("MAILGUN_SENDER_DOMAIN", ""),
}
# Database # Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
@ -204,17 +211,17 @@ MEDIA_URL = os.getenv("MEDIA_URL", "https://ojuso-media.nyc3.digitaloceanspaces.
# Cache # Cache
# https://docs.djangoproject.com/en/1.11/topics/cache/ # https://docs.djangoproject.com/en/1.11/topics/cache/
if not DEBUG: if DEBUG:
CACHES = { CACHES = {
'default': { 'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'LOCATION': 'cache:11211',
} }
} }
else: else:
CACHES = { CACHES = {
'default': { 'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache', 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'cache:11211',
} }
} }
@ -252,13 +259,9 @@ LEAFLET_CONFIG = {
}, },
} }
# Moderation
# https://django-moderation.readthedocs.io/
MODERATION_MODERATORS = ('livvy@base.nu',)
# Sentry - Error Reporting # Sentry - Error Reporting
RAVEN_CONFIG = { RAVEN_CONFIG = {
'dsn': 'https://296dda892e6e4838835a2330dd621569:10943d15104244d683fe5ccc0c898386@sentry.io/227480', 'dsn': os.getenv("RAVEN_DSN", 'https://296dda892e6e4838835a2330dd621569:10943d15104244d683fe5ccc0c898386@sentry.io/227480'),
# If you are using git, you can also automatically configure the # If you are using git, you can also automatically configure the
# release based on the git info. # release based on the git info.
'release': raven.fetch_git_sha(os.path.dirname(os.pardir)), 'release': raven.fetch_git_sha(os.path.dirname(os.pardir)),
@ -272,4 +275,4 @@ AVATAR_CLEANUP_DELETED = True
from django.contrib.messages import constants as messages from django.contrib.messages import constants as messages
MESSAGE_TAGS = { MESSAGE_TAGS = {
messages.ERROR: 'danger' messages.ERROR: 'danger'
} }

View File

@ -5,28 +5,27 @@
{% load leaflet_tags %} {% load leaflet_tags %}
{% load static %} {% load static %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html>
<head> <meta charset="utf-8">
<!-- Required meta tags --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Noto+Sans" />
<meta charset="utf-8"> <link rel="stylesheet" href="{% static 'map/bootstrap/css/bootstrap.min.css' %}" />
<title>{% block page_title %}Ojuso{% endblock %}</title> <link rel="stylesheet" href="{% static 'map/bootstrap/css/bootstrap-theme.min.css' %}" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous"/>
<link rel="stylesheet" href="{% static 'map/bootstrap/css/bootstrap.min.css' %}" />
<link rel="stylesheet" href="{% static 'map/bootstrap/css/bootstrap-theme.min.css' %}" /> <title>{% block page_title %}Ojuso{% endblock %}</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous"/> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Noto+Sans" />
{# Additional Stylesheets #}
{% block stylesheets %}
<style>
.navbar-brand {padding: 5px 15px;}
.navbar-brand > img {height: 40px;}
.dropdown-menu>li>a.no-hover:hover, .dropdown-menu>li>a.no-hover:focus {
background: red;
}
</style>
{% endblock %}
{# Additional Stylesheets #}
{% block stylesheets %}
<style>
.navbar-brand {padding: 5px 15px;}
.navbar-brand > img {height: 40px;}
.dropdown-menu>li>a.no-hover:hover, .dropdown-menu>li>a.no-hover:focus {
background: red;
}
</style>
{% endblock %}
</head>
<body> <body>
<nav class="navbar navbar-default"> <nav class="navbar navbar-default">
<div class="container-fluid"> <div class="container-fluid">
@ -51,7 +50,7 @@
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a class="btn btn-primary navbar-btn" href="{% url 'create' %}"><i class="fa fa-plus" aria-hidden="true"> New Case Study</i></a> <a class="btn btn-primary navbar-btn" href="{% url 'create' %}"><i class="fa fa-plus" aria-hidden="true"></i> New Case Study</a>
<li class="dropdown"> <li class="dropdown">
<a style="margin:-10px 0 -10px 0" class="dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a style="margin:-10px 0 -10px 0" class="dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img src="{% avatar_url user %}" class="img-circle" width="40" height="40" style="position:relative;margin-right:5px"/><span>{{user}}</span> <img src="{% avatar_url user %}" class="img-circle" width="40" height="40" style="position:relative;margin-right:5px"/><span>{{user}}</span>
@ -85,12 +84,11 @@
{% endif %} {% endif %}
{% block content %} {% block content %}
{% endblock %} {% endblock %}
</body>
{# CDN Javascript #} {# CDN Javascript #}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js" integrity="sha256-JmvOoLtYsmqlsWxa7mDSLMwa6dZ9rrIdtrrVYRnDRH0=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js" integrity="sha256-JmvOoLtYsmqlsWxa7mDSLMwa6dZ9rrIdtrrVYRnDRH0=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> {% block scripts %}{% endblock %}
{% block scripts %}{% endblock %}
</html> </html>
{% endspaceless %} {% endspaceless %}

View File

@ -1,11 +1,20 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n %} {% load i18n %}
{% load bootstrap3 %}
{% load crispy_forms_tags %}
{% block content %} {% block content %}
<form method="post" action=".">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="{% trans 'Submit' %}" /> <div class="container text-center" style="max-width:520px;">
</form> <div class="page-lead">
<h2>{% trans 'Reset your password' %}</h2>
<p class="lead">{% trans 'Enter your email address below' %}</p>
</div>
<form method="post" action=".">
{% csrf_token %}
{{ form | crispy }}
<input class="btn btn-default" type="submit" name="submit" value="{% trans 'Submit' %}">
</form>
</div>
{% endblock %} {% endblock %}

View File

@ -34,16 +34,31 @@ class UserViewSet(viewsets.ModelViewSet):
class CaseStudySerializer(gis_serializers.GeoFeatureModelSerializer): class CaseStudySerializer(gis_serializers.GeoFeatureModelSerializer):
sector_of_economy = serializers.CharField(source='get_sector_of_economy_display')
country_name = serializers.CharField(source='get_country_display')
positive_or_negative_display = serializers.CharField(source='get_positive_or_negative_display')
class Meta: class Meta:
model = CaseStudy model = CaseStudy
geo_field = "location" geo_field = "location"
fields = '__all__' fields = (
'country',
'country_name',
'entry_name',
'image',
'location',
'positive_or_negative',
'positive_or_negative_display',
'sector_of_economy',
'slug'
)
class CaseStudyViewSet(viewsets.ModelViewSet): class CaseStudyViewSet(viewsets.ModelViewSet):
queryset = CaseStudy.objects.all() queryset = CaseStudy.objects.approved()
serializer_class = CaseStudySerializer serializer_class = CaseStudySerializer
apirouter = routers.DefaultRouter() apirouter = routers.DefaultRouter()
apirouter.register(r'users', UserViewSet) apirouter.register(r'users', UserViewSet)
apirouter.register(r'case-studies', CaseStudyViewSet) apirouter.register(r'case-studies', CaseStudyViewSet)

View File

@ -4,6 +4,7 @@ boto==2.48.0
boto3==1.4.7 boto3==1.4.7
Django==1.11.6 Django==1.11.6
django-appconf==1.0.2 django-appconf==1.0.2
django-anymail==2.0
django-avatar==4.0.1 django-avatar==4.0.1
django-bootstrap3==8.2.3 django-bootstrap3==8.2.3
django-braces==1.11.0 django-braces==1.11.0
@ -14,11 +15,10 @@ django-crispy-forms==1.6.1
django-envelope==1.3 django-envelope==1.3
django-extensions==1.7.9 django-extensions==1.7.9
django-geojson==2.10.0 django-geojson==2.10.0
#django-leaflet==0.22.0 django-leaflet==0.23.0
-e git://github.com/makinacorpus/django-leaflet.git@a43acc5fed6674b413a6fab0feeb7c44e67c2ca8#egg=django-leaflet
django-moderation==0.5.0
django-multiselectfield==0.1.8 django-multiselectfield==0.1.8
django-multiupload==0.5.2 django-multiupload==0.5.2
django-phonenumber-field==2.0.0
django-registration-redux==1.6 django-registration-redux==1.6
django-storages==1.6.5 django-storages==1.6.5
djangorestframework==3.6.3 djangorestframework==3.6.3