class BaseFundraiserSerializer(serializers.ModelSerializer): """ Serializer to view/create fundraisers """ owner = UserProfileSerializer(read_only=True) project = serializers.SlugRelatedField(slug_field='slug', queryset=Project.objects) image = ImageSerializer() video_html = OEmbedField(source='video_url', maxwidth='560', maxheight='315') amount = MoneySerializer() amount_donated = MoneySerializer(read_only=True) validators = [ProjectCurrencyValidator()] class Meta: model = Fundraiser fields = ('id', 'owner', 'project', 'title', 'description', 'image', 'created', 'video_html', 'video_url', 'amount', 'amount_donated', 'deadline') def validate(self, data): if not data.get( 'deadline') or data['deadline'] > data['project'].deadline: raise serializers.ValidationError({ 'deadline': [_("Fundraiser deadline exceeds project deadline.")] }) return data
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', } )
class DonationListSerializer(BaseContributionListSerializer): amount = MoneySerializer() user = ResourceRelatedField( queryset=Member.objects.all(), default=serializers.CurrentUserDefault(), allow_null=True, required=False ) included_serializers = { 'activity': 'bluebottle.funding.serializers.TinyFundingSerializer', 'user': '******', } class Meta(BaseContributionListSerializer.Meta): model = Donation fields = BaseContributionListSerializer.Meta.fields + ('amount', 'name', 'reward', 'anonymous',) meta_fields = ('created', 'updated', ) class JSONAPIMeta(BaseContributionListSerializer.JSONAPIMeta): resource_name = 'contributions/donations' included_resources = [ 'user', 'activity', ]
class WallpostDonationSerializer(serializers.ModelSerializer): amount = MoneySerializer() user = UserPreviewSerializer() type = serializers.SerializerMethodField() class Meta(object): model = Donation fields = ( 'type', 'id', 'user', 'amount', 'name', 'fundraiser', 'reward', 'anonymous', ) def get_type(self, obj): return 'contributions/donations' def get_fields(self): """ If the donation is anonymous, we do not return the user. """ fields = super(WallpostDonationSerializer, self).get_fields() if isinstance(self.instance, Donation) and self.instance.anonymous: del fields['user'] return fields
class ProjectBudgetLineSerializer(serializers.ModelSerializer): amount = MoneySerializer() project = serializers.SlugRelatedField(slug_field='slug', queryset=Project.objects) class Meta: model = ProjectBudgetLine fields = ('id', 'project', 'description', 'amount')
class MonthlyDonationSerializer(serializers.ModelSerializer): projects = MonthlyDonationProjectSerializer(many=True, read_only=True) amount = MoneySerializer() class Meta(): model = MonthlyDonor fields = ('id', 'amount', 'iban', 'bic', 'active', 'name', 'city', 'country', 'projects')
class ProjectDonationSerializer(serializers.ModelSerializer): member = UserPreviewSerializer(source='user') date_donated = serializers.DateTimeField(source='ready') amount = MoneySerializer() siblings = serializers.IntegerField() class Meta: model = Donation fields = ('member', 'date_donated', 'amount',)
class PayoutSerializer(serializers.ModelSerializer): amount = MoneySerializer(source='amount_payable') class Meta: model = ProjectPayout fields = ('id', 'amount', 'project', 'receiver_account_number', 'receiver_account_iban', 'receiver_account_details', 'receiver_account_name', 'receiver_account_city', 'receiver_account_country')
class PayoutDonationSerializer(serializers.ModelSerializer): # For Payout service amount = MoneySerializer(source='payout_amount') class Meta(object): fields = ( 'id', 'amount', 'status' ) model = Donation
class RewardSerializer(serializers.ModelSerializer): project = serializers.SlugRelatedField(slug_field="slug", queryset=Project.objects) count = serializers.IntegerField(read_only=True) amount = MoneySerializer(min_amount=5.00) validators = [ProjectCurrencyValidator()] class Meta: model = Reward fields = ('id', 'title', 'description', 'amount', 'limit', 'project', 'count')
class LatestDonationSerializer(serializers.ModelSerializer): project = LatestDonationProjectSerializer() payment_method = serializers.SerializerMethodField() user = UserPreviewSerializer() amount = MoneySerializer() class Meta: model = Donation fields = ('amount', 'anonymous', 'created', 'fundraiser', 'id', 'name', 'payment_method', 'project', 'user') def get_payment_method(self, obj): return obj.get_payment_method()
class ManageOrderSerializer(serializers.ModelSerializer): total = MoneySerializer(read_only=True) status = serializers.ChoiceField(choices=Order.STATUS_CHOICES, read_only=True) user = serializers.PrimaryKeyRelatedField(queryset=Member.objects, required=False, allow_null=True) donations = ManageDonationSerializer(many=True, read_only=True) class Meta: model = Order fields = ('id', 'user', 'total', 'status', 'donations', 'created', 'payment_message')
class ManageOrderPaymentSerializer(serializers.ModelSerializer): status = serializers.CharField(read_only=True) status_description = serializers.CharField(read_only=True) status_code = serializers.CharField(read_only=True) amount = MoneySerializer(read_only=True) authorization_action = OrderPaymentActionSerializer(read_only=True) payment_method = serializers.CharField(required=True) integration_data = serializers.JSONField() class Meta: model = OrderPayment fields = ('id', 'order', 'payment_method', 'integration_data', 'amount', 'status', 'status_description', 'status_code', 'authorization_action')
class PreviewDonationSerializer(serializers.ModelSerializer): project = serializers.PrimaryKeyRelatedField(read_only=True) fundraiser = serializers.PrimaryKeyRelatedField( required=False, queryset=Fundraiser.objects) payment_method = serializers.SerializerMethodField() user = UserPreviewSerializer(source='public_user') amount = MoneySerializer() name = serializers.CharField(source='public_name', required=False) class Meta: model = Donation fields = ('amount', 'anonymous', 'created', 'fundraiser', 'id', 'name', 'payment_method', 'project', 'reward', 'user') def get_payment_method(self, obj): return obj.get_payment_method()
class DonationSerializer(BaseContributionSerializer): amount = MoneySerializer() user = ResourceRelatedField( queryset=Member.objects.all(), default=serializers.CurrentUserDefault(), allow_null=True, required=False ) included_serializers = { 'activity': 'bluebottle.funding.serializers.FundingListSerializer', 'user': '******', 'reward': 'bluebottle.funding.serializers.RewardSerializer', } validators = [ IsRelatedToActivity('reward'), DonationMemberValidator(), reward_amount_matches, ] class Meta(BaseContributionSerializer.Meta): model = Donation fields = BaseContributionSerializer.Meta.fields + ('amount', 'name', 'reward', 'anonymous',) class JSONAPIMeta(BaseContributionSerializer.JSONAPIMeta): resource_name = 'contributions/donations' included_resources = [ 'user', 'activity', 'reward', ] def get_fields(self): """ If the donation is anonymous, we do not return the user. """ fields = super(DonationSerializer, self).get_fields() if isinstance(self.instance, Donation) and self.instance.anonymous: del fields['user'] return fields
class ProjectCreateTemplateSerializer(serializers.ModelSerializer): default_amount_asked = MoneySerializer(min_amount=5.0) image = ImageSerializer() default_image = ImageSerializer() default_story = serializers.CharField(source='default_description') class Meta: model = ProjectCreateTemplate fields = ( 'name', 'sub_name', 'image', 'description', 'default_amount_asked', 'default_title', 'default_pitch', 'default_story', 'default_image', )
class BudgetLineSerializer(ModelSerializer): activity = ResourceRelatedField(queryset=Funding.objects.all()) amount = MoneySerializer() validators = [FundingCurrencyValidator()] included_serializers = { 'activity': 'bluebottle.funding.serializers.FundingSerializer', } class Meta(object): model = BudgetLine fields = ('activity', 'amount', 'description') class JSONAPIMeta(object): included_resources = [ 'activity', ] resource_name = 'activities/budget-lines'
class RewardSerializer(ModelSerializer): activity = ResourceRelatedField(queryset=Funding.objects.all()) count = IntegerField(read_only=True) amount = MoneySerializer(min_amount=5.00) validators = [FundingCurrencyValidator()] included_serializers = { 'activity': 'bluebottle.funding.serializers.FundingSerializer', } class Meta(object): model = Reward fields = ('id', 'title', 'description', 'amount', 'limit', 'activity', 'count') class JSONAPIMeta(object): included_resources = [ 'activity', ] resource_name = 'activities/rewards'
class ProjectPayoutSerializer(serializers.ModelSerializer): amount_asked = MoneySerializer(required=False) amount_donated = MoneyTotalSerializer(source='totals_donated', read_only=True) title = serializers.CharField(required=False) receiver_account_name = serializers.CharField(source='account_holder_name', read_only=True) receiver_account_number = serializers.CharField(source='account_number', read_only=True) receiver_account_details = serializers.CharField(source='account_details', read_only=True) receiver_account_city = serializers.CharField(source='account_holder_city', read_only=True) receiver_account_address = serializers.CharField(source='account_holder_address', read_only=True) receiver_account_country = serializers.CharField(source='account_holder_country.name', read_only=True) donations = PayoutDonationSerializer(many=True, read_only=True) status = serializers.CharField(source='payout_status') target_reached = serializers.SerializerMethodField() def get_target_reached(self, obj): return obj.status.slug == 'done-complete' class Meta: model = Project fields = ('id', 'status', 'title', 'amount_donated', 'amount_asked', 'campaign_started', 'campaign_ended', 'target_reached', 'receiver_account_number', 'receiver_account_details', 'receiver_account_name', 'receiver_account_city', 'receiver_account_address', 'receiver_account_country', 'donations' )
class ManageDonationSerializer(serializers.ModelSerializer): project = serializers.SlugRelatedField(slug_field='slug', queryset=Project.objects) fundraiser = serializers.PrimaryKeyRelatedField( required=False, allow_null=True, queryset=Fundraiser.objects) order = serializers.PrimaryKeyRelatedField(queryset=Order.objects) amount = MoneySerializer() status = serializers.CharField(read_only=True) name = serializers.CharField(required=False, allow_null=True, allow_blank=True) validators = [ProjectCurrencyValidator()] def validate_reward(self, reward): if (reward and not (self.instance and reward == self.instance.reward) and (reward.limit and reward.count >= reward.limit)): raise serializers.ValidationError('Reward out of stock') return reward def validate(self, data): if 'reward' in data: if data['reward'] and data['reward'].amount.currency != data[ 'amount'].currency: raise serializers.ValidationError( 'Currency must match reward currency') if data['reward'] and data['reward'].amount.amount > data[ 'amount'].amount: raise serializers.ValidationError( 'Amounts can not be less than the reward amount') return data class Meta: model = Donation fields = ('amount', 'anonymous', 'completed', 'created', 'fundraiser', 'id', 'name', 'order', 'project', 'reward', 'status')
class PayoutDonationSerializer(serializers.ModelSerializer): amount = MoneySerializer() status = serializers.CharField(source='order.status') confirmed = serializers.CharField(source='order.confirmed') completed = serializers.CharField(source='order.completed') type = serializers.CharField(source='order.order_type') payment_method = serializers.SerializerMethodField(source='order.order_payment.payment_method') def get_payment_method(self, instance): if instance.order.order_type == 'recurring': return 'docdata-directdebit' if instance.order.order_payment: return re.sub('([A-Z]+)', r'-\1', instance.order.order_payment.payment_method).lower() return '-unknown-' class Meta: model = Donation fields = ('id', 'type', 'amount', 'status', 'confirmed', 'completed', 'payment_method')
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',)
class DonationCreateSerializer(DonationSerializer): amount = MoneySerializer() class Meta(DonationSerializer.Meta): model = Donation fields = DonationSerializer.Meta.fields + ('client_secret', )
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', )
class BasicProjectBudgetLineSerializer(serializers.ModelSerializer): amount = MoneySerializer() class Meta: model = ProjectBudgetLine fields = ('id', 'description', 'amount')
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
def setUp(self): self.serializer = MoneySerializer()