Ejemplo n.º 1
0
class EventListSerializer(BaseActivityListSerializer):
    permissions = ResourcePermissionField('event-detail', view_args=('pk', ))

    class Meta(BaseActivityListSerializer.Meta):
        model = Event
        fields = BaseActivityListSerializer.Meta.fields + (
            'capacity',
            'start',
            'local_start',
            'duration',
            'is_online',
            'location',
            'location_hint',
            'permissions',
            'registration_deadline',
        )

    class JSONAPIMeta(BaseActivityListSerializer.JSONAPIMeta):
        included_resources = [
            'location',
        ]
        resource_name = 'activities/events'

    included_serializers = dict(
        BaseActivitySerializer.included_serializers, **{
            'location': 'bluebottle.geo.serializers.GeolocationSerializer',
        })
Ejemplo n.º 2
0
class BaseContributionSerializer(ModelSerializer):
    status = FSMField(read_only=True)
    user = ResourceRelatedField(read_only=True,
                                default=serializers.CurrentUserDefault())

    permissions = ResourcePermissionField('initiative-detail',
                                          view_args=('pk', ))
    transitions = AvailableTransitionsField(source='states')

    included_serializers = {
        'activity': 'bluebottle.activities.serializers.ActivityListSerializer',
        'user': '******',
    }

    class Meta(object):
        model = Contribution
        fields = (
            'user',
            'activity',
            'status',
        )
        meta_fields = (
            'permissions',
            'transitions',
            'created',
            'updated',
        )

    class JSONAPIMeta(object):
        included_resources = [
            'user',
            'activity',
        ]
        resource_name = 'contributions'
Ejemplo n.º 3
0
class AssignmentListSerializer(BaseActivityListSerializer):
    permissions = ResourcePermissionField('assignment-detail',
                                          view_args=('pk', ))

    class Meta(BaseActivityListSerializer.Meta):
        model = Assignment
        fields = BaseActivityListSerializer.Meta.fields + (
            'is_online',
            'date',
            'local_date',
            'end_date_type',
            'registration_deadline',
            'capacity',
            'expertise',
            'duration',
            'location',
            'permissions',
            'preparation',
        )

    class JSONAPIMeta(BaseActivityListSerializer.JSONAPIMeta):
        resource_name = 'activities/assignments'
        included_resources = [
            'location',
            'expertise',
        ]

    included_serializers = dict(
        BaseActivityListSerializer.included_serializers, **{
            'expertise': 'bluebottle.assignments.serializers.SkillSerializer',
            'location': 'bluebottle.geo.serializers.GeolocationSerializer',
        })
Ejemplo n.º 4
0
class FundingListSerializer(BaseActivityListSerializer):
    target = MoneySerializer(required=False, allow_null=True)
    permissions = ResourcePermissionField('funding-detail', view_args=('pk',))
    amount_raised = MoneySerializer(read_only=True)
    amount_donated = MoneySerializer(read_only=True)
    amount_matching = MoneySerializer(read_only=True)

    class Meta(BaseActivityListSerializer.Meta):
        model = Funding
        fields = BaseActivityListSerializer.Meta.fields + (
            'country',
            'deadline',
            'duration',
            'target',
            'amount_donated',
            'amount_matching',
            'amount_raised',
        )

    class JSONAPIMeta(BaseActivityListSerializer.JSONAPIMeta):
        resource_name = 'activities/fundings'

    included_serializers = dict(
        BaseActivitySerializer.included_serializers,
        **{
            'location': 'bluebottle.geo.serializers.GeolocationSerializer',
        }
    )
Ejemplo n.º 5
0
class OrganizationSerializer(NoCommitMixin, ModelSerializer):
    description = serializers.CharField(required=False, allow_blank=True)
    slug = serializers.SlugField(allow_null=True, required=False)
    name = serializers.CharField(required=True)
    website = serializers.CharField(allow_blank=True, required=False)
    logo = ImageSerializer(required=False, allow_null=True)

    permissions = ResourcePermissionField('organization_detail', view_args=('pk',))

    errors = ValidationErrorsField()
    required = RequiredErrorsField()

    included_serializers = {
        'owner': 'bluebottle.initiatives.serializers.MemberSerializer',
    }

    class Meta(object):
        model = Organization
        fields = (
            'id', 'name', 'slug', 'description', 'website', 'owner', 'logo',
            'required', 'errors',
        )

        meta_fields = ['created', 'updated', 'errors', 'required', 'permissions']

    class JSONAPIMeta(object):
        resource_name = 'organizations'
        included_resources = ['owner', ]
Ejemplo n.º 6
0
class AssignmentSerializer(BaseActivitySerializer):
    permissions = ResourcePermissionField('assignment-detail',
                                          view_args=('pk', ))
    contributions = FilteredRelatedField(many=True,
                                         filter_backend=ApplicantListFilter)

    def get_fields(self):
        fields = super(AssignmentSerializer, self).get_fields()
        user = self.context['request'].user

        if (not user.is_authenticated or (self.instance and (user not in [
                self.instance.owner, self.instance.initiative.owner,
                self.instance.initiative.activity_manager
        ] and not len(self.instance.applicants.filter(user=user))))):
            del fields['online_meeting_url']

        return fields

    class Meta(BaseActivitySerializer.Meta):
        model = Assignment
        fields = BaseActivitySerializer.Meta.fields + (
            'is_online',
            'online_meeting_url',
            'date',
            'local_date',
            'end_date_type',
            'registration_deadline',
            'capacity',
            'expertise',
            'duration',
            'location',
            'permissions',
            'contributions',
            'start_time',
            'preparation',
        )

    class JSONAPIMeta(BaseActivitySerializer.JSONAPIMeta):
        resource_name = 'activities/assignments'
        included_resources = BaseActivitySerializer.JSONAPIMeta.included_resources + [
            'location', 'expertise', 'contributions', 'contributions.user',
            'contributions.document'
        ]

    included_serializers = dict(
        BaseActivitySerializer.included_serializers, **{
            'expertise':
            'bluebottle.assignments.serializers.SkillSerializer',
            'location':
            'bluebottle.geo.serializers.GeolocationSerializer',
            'contributions':
            'bluebottle.assignments.serializers.ApplicantSerializer',
        })
Ejemplo n.º 7
0
class InitiativeListSerializer(ModelSerializer):
    status = FSMField(read_only=True)
    image = ImageField(required=False, allow_null=True)
    owner = ResourceRelatedField(read_only=True)
    permissions = ResourcePermissionField('initiative-detail',
                                          view_args=('pk', ))
    activity_manager = ResourceRelatedField(read_only=True)
    slug = serializers.CharField(read_only=True)
    story = SafeField(required=False, allow_blank=True, allow_null=True)
    title = serializers.CharField(allow_blank=True)
    transitions = AvailableTransitionsField(source='states')

    included_serializers = {
        'categories': 'bluebottle.initiatives.serializers.CategorySerializer',
        'image':
        'bluebottle.initiatives.serializers.InitiativeImageSerializer',
        'owner': 'bluebottle.initiatives.serializers.MemberSerializer',
        'activity_manager':
        'bluebottle.initiatives.serializers.MemberSerializer',
        'place': 'bluebottle.geo.serializers.GeolocationSerializer',
        'location': 'bluebottle.geo.serializers.LocationSerializer',
        'theme': 'bluebottle.initiatives.serializers.ThemeSerializer',
    }

    class Meta(object):
        model = Initiative
        fsm_fields = ['status']
        fields = ('id', 'title', 'pitch', 'categories', 'owner',
                  'activity_manager', 'slug', 'has_organization',
                  'transitions', 'story', 'image', 'theme', 'place',
                  'location')

        meta_fields = (
            'permissions',
            'status',
            'created',
            'transitions',
        )

    class JSONAPIMeta(object):
        included_resources = [
            'owner',
            'activity_manager',
            'categories',
            'theme',
            'place',
            'location',
            'image',
            'organization',
        ]
        resource_name = 'initiatives'
Ejemplo n.º 8
0
class EventSerializer(NoCommitMixin, BaseActivitySerializer):
    permissions = ResourcePermissionField('event-detail', view_args=('pk', ))
    contributions = FilteredRelatedField(many=True,
                                         filter_backend=ParticipantListFilter)
    links = serializers.SerializerMethodField()

    def get_links(self, instance):
        return {
            'ical': reverse_signed('event-ical', args=(instance.pk, )),
            'google': instance.google_calendar_link,
            'outlook': instance.outlook_link,
        }

    def get_fields(self):
        fields = super(EventSerializer, self).get_fields()
        user = self.context['request'].user

        if (not user.is_authenticated or (self.instance and (user not in [
                self.instance.owner, self.instance.initiative.owner,
                self.instance.initiative.activity_manager
        ] and not len(self.instance.participants.filter(user=user))))):
            del fields['online_meeting_url']

        return fields

    class Meta(BaseActivitySerializer.Meta):
        model = Event
        fields = BaseActivitySerializer.Meta.fields + (
            'capacity', 'start', 'local_start', 'duration', 'is_online',
            'online_meeting_url', 'location', 'location_hint', 'permissions',
            'registration_deadline', 'contributions', 'links')

    class JSONAPIMeta(BaseActivitySerializer.JSONAPIMeta):
        included_resources = BaseActivitySerializer.JSONAPIMeta.included_resources + [
            'location', 'contributions', 'contributions.user'
        ]
        resource_name = 'activities/events'

    included_serializers = dict(
        BaseActivitySerializer.included_serializers, **{
            'location': 'bluebottle.geo.serializers.GeolocationSerializer',
            'contributions':
            'bluebottle.events.serializers.ParticipantSerializer',
        })
Ejemplo n.º 9
0
class BaseActivityListSerializer(ModelSerializer):
    title = serializers.CharField(allow_blank=True, required=False)
    status = FSMField(read_only=True)
    permissions = ResourcePermissionField('activity-detail',
                                          view_args=('pk', ))
    owner = ResourceRelatedField(read_only=True)
    is_follower = serializers.SerializerMethodField()
    type = serializers.CharField(read_only=True,
                                 source='JSONAPIMeta.resource_name')
    stats = serializers.OrderedDict(read_only=True)
    goals = ResourceRelatedField(required=False,
                                 many=True,
                                 queryset=ImpactGoal.objects.all())
    slug = serializers.CharField(read_only=True)

    included_serializers = {
        'initiative':
        'bluebottle.initiatives.serializers.InitiativeListSerializer',
        'image': 'bluebottle.activities.serializers.ActivityImageSerializer',
        'owner': 'bluebottle.initiatives.serializers.MemberSerializer',
        'goals': 'bluebottle.impact.serializers.ImpactGoalSerializer',
        'goals.type': 'bluebottle.impact.serializers.ImpactTypeSerializer',
    }

    def get_is_follower(self, instance):
        user = self.context['request'].user
        return bool(user.is_authenticated) and instance.followers.filter(
            user=user).exists()

    class Meta(object):
        model = Activity
        fields = (
            'type',  # Needed for old style API endpoints like pages / page blocks
            'slug',
            'id',
            'image',
            'initiative',
            'owner',
            'title',
            'description',
            'is_follower',
            'status',
            'stats',
            'goals',
        )

        meta_fields = (
            'permissions',
            'created',
            'updated',
        )

    class JSONAPIMeta(object):
        included_resources = [
            'owner',
            'initiative',
            'image',
            'initiative.image',
            'initiative.location',
            'initiative.place',
            'goals',
            'goals.type',
        ]
        resource_name = 'activities'
Ejemplo n.º 10
0
class FundingSerializer(NoCommitMixin, BaseActivitySerializer):
    target = MoneySerializer(required=False, allow_null=True)
    amount_raised = MoneySerializer(read_only=True)
    amount_donated = MoneySerializer(read_only=True)
    amount_matching = MoneySerializer(read_only=True)
    rewards = RewardSerializer(many=True, required=False)
    budget_lines = BudgetLineSerializer(many=True, required=False)
    payment_methods = SerializerMethodResourceRelatedField(
        read_only=True, many=True, source='get_payment_methods', model=PaymentMethod
    )
    contributions = FilteredRelatedField(many=True, filter_backend=DonationListFilter)
    permissions = ResourcePermissionField('funding-detail', view_args=('pk',))

    bank_account = PolymorphicResourceRelatedField(
        BankAccountSerializer,
        queryset=BankAccount.objects.all(),
        required=False,
        allow_null=True
    )

    supporters_export_url = PrivateFileSerializer(
        'funding-supporters-export', url_args=('pk', ),
        filename='supporters.csv',
        permission=CanExportSupportersPermission,
        read_only=True
    )
    account_info = serializers.DictField(source='bank_account.public_data', read_only=True)

    def get_fields(self):
        fields = super(FundingSerializer, self).get_fields()

        if not self.context['request'].user in [
            self.instance.owner,
            self.instance.initiative.owner,
            self.instance.initiative.activity_manager
        ]:
            del fields['bank_account']
            del fields['required']
            del fields['errors']
        return fields

    class Meta(BaseActivitySerializer.Meta):
        model = Funding
        fields = BaseActivitySerializer.Meta.fields + (
            'country',
            'deadline',
            'duration',
            'target',
            'amount_donated',
            'amount_matching',
            'amount_raised',
            'account_info',

            'rewards',
            'payment_methods',
            'budget_lines',
            'contributions',
            'bank_account',
            'supporters_export_url',
        )

    class JSONAPIMeta(BaseActivitySerializer.JSONAPIMeta):
        included_resources = BaseActivitySerializer.JSONAPIMeta.included_resources + [
            'payment_methods',
            'rewards',
            'budget_lines',
            'contributions',
            'contributions.user',
            'bank_account',
        ]
        resource_name = 'activities/fundings'

    included_serializers = dict(
        BaseActivitySerializer.included_serializers,
        **{
            'rewards': 'bluebottle.funding.serializers.BudgetLineSerializer',
            'budget_lines': 'bluebottle.funding.serializers.RewardSerializer',
            'contributions': 'bluebottle.funding.serializers.DonationSerializer',
            'bank_account': 'bluebottle.funding.serializers.BankAccountSerializer',
            'payment_methods': 'bluebottle.funding.serializers.PaymentMethodSerializer',
        }
    )

    def get_payment_methods(self, obj):
        if not obj.bank_account:
            return []

        methods = obj.bank_account.payment_methods

        request = self.context['request']

        if request.user.is_authenticated and request.user.can_pledge:
            methods.append(
                PaymentMethod(
                    provider='pledge',
                    code='pledge',
                    name=_('Pledge'),
                    currencies=[
                        'EUR', 'USD', 'NGN', 'UGX', 'KES', 'XOF', 'BGN'
                    ]
                )
            )

        return methods
Ejemplo n.º 11
0
class InitiativeSerializer(NoCommitMixin, ModelSerializer):
    status = FSMField(read_only=True)
    image = ImageField(required=False, allow_null=True)
    owner = ResourceRelatedField(read_only=True)
    permissions = ResourcePermissionField('initiative-detail',
                                          view_args=('pk', ))
    reviewer = ResourceRelatedField(read_only=True)
    activity_manager = ResourceRelatedField(read_only=True)
    activities = FilteredPolymorphicResourceRelatedField(
        filter_backend=ActivityFilter,
        polymorphic_serializer=ActivityListSerializer,
        many=True,
        read_only=True)
    slug = serializers.CharField(read_only=True)
    story = SafeField(required=False, allow_blank=True, allow_null=True)
    title = serializers.CharField(allow_blank=True)

    errors = ValidationErrorsField()
    required = RequiredErrorsField()

    stats = serializers.ReadOnlyField()
    transitions = AvailableTransitionsField(source='states')

    included_serializers = {
        'categories':
        'bluebottle.initiatives.serializers.CategorySerializer',
        'image':
        'bluebottle.initiatives.serializers.InitiativeImageSerializer',
        'owner':
        'bluebottle.initiatives.serializers.MemberSerializer',
        'reviewer':
        'bluebottle.initiatives.serializers.MemberSerializer',
        'promoter':
        'bluebottle.initiatives.serializers.MemberSerializer',
        'activity_manager':
        'bluebottle.initiatives.serializers.MemberSerializer',
        'place':
        'bluebottle.geo.serializers.GeolocationSerializer',
        'location':
        'bluebottle.geo.serializers.LocationSerializer',
        'theme':
        'bluebottle.initiatives.serializers.ThemeSerializer',
        'organization':
        'bluebottle.organizations.serializers.OrganizationSerializer',
        'organization_contact':
        'bluebottle.organizations.serializers.OrganizationContactSerializer',
        'activities':
        'bluebottle.activities.serializers.ActivityListSerializer',
        'activities.location':
        'bluebottle.geo.serializers.GeolocationSerializer',
        'activities.image':
        'bluebottle.activities.serializers.ActivityImageSerializer',
        'activities.goals':
        'bluebottle.impact.serializers.ImpactGoalSerializer',
        'activities.goals.type':
        'bluebottle.impact.serializers.ImpactTypeSerializer',
    }

    class Meta(object):
        model = Initiative
        fsm_fields = ['status']
        fields = (
            'id',
            'title',
            'pitch',
            'categories',
            'owner',
            'reviewer',
            'promoter',
            'activity_manager',
            'slug',
            'has_organization',
            'organization',
            'organization_contact',
            'story',
            'video_url',
            'image',
            'theme',
            'place',
            'location',
            'activities',
            'errors',
            'required',
            'stats',
        )

        meta_fields = (
            'permissions',
            'transitions',
            'status',
            'created',
            'required',
            'errors',
            'stats',
        )

    class JSONAPIMeta(object):
        included_resources = [
            'owner', 'reviewer', 'promoter', 'activity_manager', 'categories',
            'theme', 'place', 'location', 'image', 'organization',
            'organization_contact', 'activities', 'activities.image',
            'activities.location', 'activities.goals', 'activities.goals.type'
        ]
        resource_name = 'initiatives'
Ejemplo n.º 12
0
class ManageProjectSerializer(serializers.ModelSerializer):
    id = serializers.CharField(source='slug', read_only=True)

    account_bic = serializers.CharField(required=False, allow_blank=True, allow_null=True)
    account_details = serializers.CharField(required=False, allow_blank=True, allow_null=True)
    amount_asked = MoneySerializer(required=False, allow_null=True)
    amount_donated = MoneySerializer(read_only=True)
    amount_needed = MoneySerializer(read_only=True)
    amount_cancelled = MoneySerializer(read_only=True)
    budget_lines = ProjectBudgetLineSerializer(many=True, source='projectbudgetline_set', read_only=True)
    currencies = serializers.JSONField(read_only=True)
    categories = serializers.SlugRelatedField(many=True, read_only=True, slug_field='slug')
    documents = ProjectDocumentSerializer(many=True, read_only=True)
    editable = serializers.BooleanField(read_only=True)
    image = ImageSerializer(required=False, allow_null=True)
    is_funding = serializers.ReadOnlyField()
    location = serializers.PrimaryKeyRelatedField(required=False, allow_null=True, queryset=Location.objects)
    people_needed = serializers.IntegerField(read_only=True)
    people_registered = serializers.IntegerField(read_only=True)
    pitch = serializers.CharField(required=False, allow_null=True)
    promoter = UserProfileSerializer(read_only=True)
    slug = serializers.CharField(read_only=True)
    status = serializers.PrimaryKeyRelatedField(required=False, allow_null=True, queryset=ProjectPhase.objects)
    story = SafeField(required=False, allow_blank=True)
    task_manager = UserProfileSerializer(read_only=True)
    owner = UserProfileSerializer(read_only=True)
    tasks = ManageTaskSerializer(many=True, source='task_set', read_only=True)
    url = serializers.HyperlinkedIdentityField(view_name='project_manage_detail', lookup_field='slug')
    video_html = OEmbedField(source='video_url', maxwidth='560', maxheight='315')
    viewable = serializers.BooleanField(read_only=True)
    permissions = ResourcePermissionField('project_manage_detail', view_args=('slug', ))
    related_permissions = ProjectPermissionsSerializer(read_only=True)

    latitude = serializers.FloatField(source='projectlocation.latitude', required=False, allow_null=True)
    longitude = serializers.FloatField(source='projectlocation.longitude', required=False, allow_null=True)
    project_location = ProjectLocationSerializer(read_only=True, source='projectlocation')

    editable_fields = ('pitch', 'story', 'image', 'video_url', 'projectlocation')

    @staticmethod
    def validate_account_number(value):

        if value:
            country_code = value[:2]
            digits_regex = re.compile('\d{2}')
            check_digits = value[2:4]

            # Only try iban validaton when the field matches start of
            # iban format as the field can also contain non-iban
            # account numbers.
            # Expecting something like: NL18xxxxxxxxxx
            iban_validator = IBANValidator()
            if country_code in iban_validator.validation_countries.keys() and \
                    digits_regex.match(check_digits):
                iban_validator(value)
        return value

    def validate_status(self, value):
        if not value:
            value = ProjectPhase.objects.order_by('sequence').all()[0]
        else:
            """
            Don't let the owner set a status with a sequence number higher
            than 2
            They can set 1: plan-new or 2: plan-submitted

            TODO: This needs work. Maybe we could use a FSM for the project
                  status
                  transitions, e.g.:
                      https://pypi.python.org/pypi/django-fsm/1.2.0

            TODO: what to do if the expected status (plan-submitted) is
                  not found?! Hard fail?
            """
            submit_status = ProjectPhase.objects.get(slug='plan-submitted')
            new_status = ProjectPhase.objects.get(slug='plan-new')
            needs_work_status = ProjectPhase.objects.get(
                slug='plan-needs-work')

            proposed_status = value
            current_status = None

            # Get the current status or the first if not found
            try:
                current_status = Project.objects.get(slug=self.initial_data['slug']).status
            except (Project.DoesNotExist, KeyError):
                current_status = ProjectPhase.objects.order_by(
                    'sequence').all()[0]

            if current_status and proposed_status:
                """
                These are possible combinations of current v. proposed status
                which are permitted:
                1) the current status is the same as the proposed status
                2) the current is new or needs work and the proposed
                   is submitted
                """
                if proposed_status == current_status:
                    return value
                if proposed_status != submit_status or current_status not in [new_status, needs_work_status]:
                    raise serializers.ValidationError(_("You can not change the project state."))
        return value

    def validate(self, data):
        if self.instance and self.instance.status.slug in ('campaign', 'voting'):
            # When project is running, only a subset of the fields canb be changed
            for field, value in data.items():
                current = getattr(self.instance, field)

                if field not in self.editable_fields:
                    try:
                        # If we check a many to many field, make convert both sides to a set
                        current = set(current.all())
                        value = set(value)
                    except (AttributeError, TypeError):
                        # normal field: do nothing
                        pass

                    if value != current:
                        raise serializers.ValidationError(
                            _('Not allowed to edit {} when project is running').format(field)
                        )
                self.instance.campaign_edited = timezone.now()

        return data

    def update(self, instance, validated_data):
        if 'projectlocation' in validated_data:
            location = validated_data.pop('projectlocation')

            for field, value in location.items():
                setattr(instance.projectlocation, field, value)

            instance.projectlocation.save()

        return super(ManageProjectSerializer, self).update(instance, validated_data)

    def create(self, validated_data):
        location_data = None
        if 'projectlocation' in validated_data:
            location_data = validated_data.pop('projectlocation')

        instance = super(ManageProjectSerializer, self).create(validated_data)
        if location_data:
            for field, value in location_data.items():
                setattr(instance.projectlocation, field, value)

            instance.projectlocation.save()

        return instance

    class Meta:
        model = Project
        fields = ('id',
                  'account_bank_country',
                  'account_details',
                  'account_bic',
                  'account_holder_address',
                  'account_holder_city',
                  'account_holder_country',
                  'account_holder_name',
                  'account_holder_postal_code',
                  'account_number',
                  'amount_asked',
                  'amount_donated',
                  'amount_needed',
                  'amount_cancelled',
                  'budget_lines',
                  'categories',
                  'country',
                  'created',
                  'currencies',
                  'deadline',
                  'description',
                  'documents',
                  'editable',
                  'image',
                  'is_funding',
                  'language',
                  'latitude',
                  'location',
                  'longitude',
                  'project_location',
                  'organization',
                  'people_needed',
                  'people_registered',
                  'pitch',
                  'place',
                  'project_type',
                  'promoter',
                  'slug',
                  'status',
                  'story',
                  'task_manager',
                  'owner',
                  'tasks',
                  'theme',
                  'title',
                  'url',
                  'video_html',
                  'video_url',
                  'permissions',
                  'related_permissions',
                  'viewable',)
Ejemplo n.º 13
0
class ProjectSerializer(serializers.ModelSerializer):
    id = serializers.CharField(source='slug', read_only=True)
    addons = ProjectAddOnSerializer(many=True)
    amount_asked = MoneySerializer()
    amount_donated = MoneySerializer()
    amount_extra = MoneySerializer()
    amount_needed = MoneySerializer()
    amount_cancelled = MoneySerializer(read_only=True)
    budget_lines = BasicProjectBudgetLineSerializer(many=True, source='projectbudgetline_set', read_only=True)
    categories = serializers.SlugRelatedField(slug_field='slug', many=True, queryset=Category.objects)
    country = ProjectCountrySerializer()
    currencies = serializers.JSONField(read_only=True)
    has_voted = serializers.SerializerMethodField()
    image = ImageSerializer(required=False)
    is_funding = serializers.ReadOnlyField()
    location = serializers.PrimaryKeyRelatedField(required=False, queryset=Location.objects)
    organization = OrganizationPreviewSerializer(read_only=True)
    owner = UserProfileSerializer()
    people_needed = serializers.ReadOnlyField()
    people_registered = serializers.ReadOnlyField()
    permissions = ResourcePermissionField('project_detail', view_args=('slug',))
    promoter = UserProfileSerializer(read_only=True)
    related_permissions = ProjectPermissionsSerializer(read_only=True)
    story = SafeField()
    supporter_count = serializers.IntegerField()
    task_manager = UserProfileSerializer(read_only=True)
    video_html = OEmbedField(source='video_url', maxwidth='560', maxheight='315')
    vote_count = serializers.IntegerField()
    latitude = serializers.FloatField(source='projectlocation.latitude')
    longitude = serializers.FloatField(source='projectlocation.longitude')
    project_location = ProjectLocationSerializer(read_only=True, source='projectlocation')
    supporters_export_url = PrivateFileSerializer(
        'project-supporters-export', url_args=('slug', ), permission=CanExportSupportersPermission,
        read_only=True
    )

    def __init__(self, *args, **kwargs):
        super(ProjectSerializer, self).__init__(*args, **kwargs)

    def get_has_voted(self, obj):
        return Vote.has_voted(self.context['request'].user, obj)

    class Meta:
        model = Project
        fields = ('id',
                  'addons',
                  'allow_overfunding',
                  'amount_asked',
                  'amount_donated',
                  'amount_extra',
                  'amount_needed',
                  'amount_cancelled',
                  'budget_lines',
                  'categories',
                  'celebrate_results',
                  'country',
                  'created',
                  'currencies',
                  'deadline',
                  'description',
                  'full_task_count',
                  'has_voted',
                  'image',
                  'is_funding',
                  'language',
                  'latitude',
                  'location',
                  'longitude',
                  'project_location',
                  'open_task_count',
                  'organization',
                  'owner',
                  'people_needed',
                  'people_registered',
                  'permissions',
                  'pitch',
                  'place',
                  'project_type',
                  'promoter',
                  'realized_task_count',
                  'related_permissions',
                  'status',
                  'status',
                  'story',
                  'supporter_count',
                  'task_count',
                  'task_manager',
                  'theme',
                  'title',
                  'video_html',
                  'video_url',
                  'vote_count',
                  'voting_deadline',
                  'supporters_export_url',
                  )