Пример #1
0
class VolunteeringReportSchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = VolunteeringReport
        load_instance = True
        ordered = True
        include_fk = True
        include_relationships = True

    reporter_email = ma.Str(dump_only=True)
    application_id = ma.Int(dump_only=True)
    application = ma.Nested('ApplicationActivitySchema',
                            data_key='application_on')
    rating = ma.Int(validate=validate.Range(min=1, max=5))
Пример #2
0
class AccountSchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = Account
        load_instance = True
        ordered = True
        include_relationships = True

    def get_csrf_token(self, _account):
        return self.context.get('csrf_token')

    balance = ma.Int()
    csrf_token = ma.Method(serialize='get_csrf_token', dump_only=True)
Пример #3
0
class ProductSchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = Product
        load_instance = True
        ordered = True
        include_relationships = True

    varieties = ma.Nested('VarietySchema',
                          many=True,
                          validate=validate.Length(min=1))
    name = ma.Str(validate=validate.Length(min=1, max=128))
    type = ma.Str(validate=validate.Length(min=1, max=128), allow_none=True)
    description = ma.Str(validate=validate.Length(max=1024))
    price = ma.Int(validate=validate.Range(min=1))
Пример #4
0
class ProjectSchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = Project
        load_instance = True
        ordered = True
        include_relationships = True

    name = ma.Str(allow_none=True,
                  validate=validate.Length(max=128),
                  error_messages={
                      'validator_failed':
                      'The name must be below 128 characters.'
                  })
    creator = ma.Nested('AccountSchema', only=('full_name', 'email'))
    image_id = ma.Int(allow_none=True)
    review_status = EnumField(ReviewStatus)
    lifetime_stage = EnumField(LifetimeStage)
    activities = ma.Nested('ActivitySchema', many=True)
    moderators = ma.Nested('AccountSchema',
                           only=('full_name', 'email'),
                           many=True)
    start_date = ma.DateTime()
    end_date = ma.DateTime()
Пример #5
0
class VarietySchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = Variety
        load_instance = True
        ordered = True
        include_fk = True
        include_relationships = True

    @pre_load
    def create_stock_change(self, data, **_kwargs):
        """Convert the integer `amount` property into a stock change."""
        if 'stock_changes' in data:
            raise ValidationError(
                'The stock changes are not to be specified explicitly.')

        if self.context.get('update', False):
            return data

        if 'amount' not in data:
            raise ValidationError('The amount for a variety is not specified.')

        amount = data.pop('amount')
        data['stock_changes'] = [{
            'amount': amount,
            'account_email': self.context['user'].email,
            'status': 'carried_out',
        }]
        return data

    @pre_load
    def normalize_color(self, data, **_kwargs):
        """Normalize the color value."""
        if 'color' not in data:
            if self.context.get('update', False):
                return data
            raise ValidationError('The color must be specified.')

        if data['color'] is None:
            return data

        if data['color'].startswith('#'):
            data['color'] = data['color'][1:].upper()

        if len(data['color']) != 6:
            raise ValidationError(
                f'The color value is {len(data["color"])} characters long, 6 expected.'
            )

        return data

    @pre_load
    def enumerate_images(self, data, **_kwargs):
        """Convert the array of URL strings to an array of image objects with order."""
        if self.context.get('update', False):
            if 'images' in data:
                data['images'] = [{
                    'order': idx,
                    'image_id': id
                } for (idx, id) in enumerate(data['images'], start=1)]
        else:
            try:
                data['images'] = [{
                    'order': idx,
                    'image_id': id
                } for (idx, id) in enumerate(data['images'], start=1)]
            except KeyError:
                raise ValidationError('Images must be specified.')
        return data

    @post_dump
    def format_color(self, data, **_kwargs):
        """Add a '#' to the color value."""
        if data['color'] is not None:
            data['color'] = '#' + data['color']
        return data

    @post_dump
    def flatten_images(self, data, **_kwargs):
        """Convert an array of image objects with order into a flat array of URL strings."""
        if 'images' not in data:
            return data

        data['images'] = [
            image["image_id"]
            for image in sorted(data['images'], key=lambda x: x['order'])
        ]
        return data

    images = ma.Nested('ProductImageSchema', many=True)
    stock_changes = ma.Nested('StockChangeSchema', many=True)
    amount = ma.Int(dump_only=True)
    purchases = ma.Int(dump_only=True)
Пример #6
0
class ActivitySchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = Activity
        load_instance = True
        ordered = True
        include_relationships = True

    @pre_load
    def unwrap_dates(self, data, **_kwargs):
        """Expand the {"start": , "end": } dates object into two separate properties."""
        if 'timeframe' not in data:
            return data

        try:
            dates = data.pop('timeframe')
            data['start_date'] = dates['start']
            data['end_date'] = dates['end']
        except KeyError:
            raise ValidationError("The date range has a wrong format.")

        return data

    @post_dump
    def wrap_dates(self, data, **_kwargs):
        """Collapse the two date properties into the {"start": , "end": } dates object."""
        if 'start_date' not in data:
            return data

        data['timeframe'] = {
            'start': data.pop('start_date'),
            'end': data.pop('end_date')
        }
        return data

    @validates_schema
    def work_hours_mutex(self, data, **_kwargs):
        """Ensure that working hours aren't specified along with the reward_rate."""
        if 'work_hours' in data and 'reward_rate' in data:
            raise ValidationError(
                'Working hours and reward rate are mutually exclusive.')

    @validates_schema
    def valid_date_range(self, data, **_kwargs):
        """Ensure that the start date is not beyond the end date."""
        if data.get('start_date') is not None and data.get(
                'end_date') is not None:
            if data['start_date'] > data['end_date']:
                raise ValidationError('The start date is beyond the end date.')

    def get_applications(self, activity):
        """Retrieve the applications for a particular activity.
           For non-moderators will only return the approved applications."""
        fields = ['id', 'applicant', 'status', 'application_time']
        filtering = {
            'activity_id': activity.id,
            'status': ApplicationStatus.approved
        }

        if 'user' not in self.context or not self.context[
                'user'].is_authenticated:
            return None

        if self.context['user'] in activity.project.moderators or self.context[
                'user'].is_admin:
            filtering.pop('status')
            fields.append('telegram_username')
            fields.append('comment')
            fields.append('actual_hours')
            fields.append('feedback')
            fields.append('reports')

        appl_schema = ApplicationSchema(only=fields, many=True)
        applications = Application.query.filter_by(**filtering)
        return appl_schema.dump(applications.all())

    def get_existing_application(self, activity):
        """Using the user information from the context, provide a shorthand
        for the existing application of a volunteer."""
        appl_schema = ApplicationSchema(only=('id', 'telegram_username',
                                              'comment', 'actual_hours',
                                              'status', 'feedback'))
        if 'user' in self.context and self.context['user'].is_authenticated:
            application = Application.query.filter_by(
                applicant_email=self.context['user'].email,
                activity_id=activity.id).one_or_none()
            if application is None:
                return None
            return appl_schema.dump(application)
        return None

    working_hours = ma.Int(allow_none=True, validate=validate.Range(min=1))
    reward_rate = ma.Int(allow_none=True, validate=validate.Range(min=1))
    people_required = ma.Int(allow_none=True, validate=validate.Range(min=0))
    start_date = ma.AwareDateTime(allow_none=True, format='iso')
    end_date = ma.AwareDateTime(allow_none=True, format='iso')
    application_deadline = ma.AwareDateTime(allow_none=True, format='iso')
    competences = ma.Pluck(CompetenceSchema,
                           'id',
                           many=True,
                           validate=validate.Length(0, 3))
    vacant_spots = ma.Int(dump_only=True)
    applications = ma.Method(serialize='get_applications', dump_only=True)
    existing_application = ma.Method(serialize='get_existing_application',
                                     dump_only=True)