"""Module containing scheduled operation related views.""" # vim: set tw=80 ts=4 sw=4 sts=4: from flask_jwt_extended import jwt_required from flask_restplus import Namespace, Resource, fields from sqlalchemy import true from ..models import db from ..models.accounts import Account from ..models.operations import Operation from ..models.scheduled_operations import ScheduledOperation # pylint: disable=invalid-name ns = Namespace( 'scheduled_operation', description='Scheduled operation management' ) # Scheduled operation model. scheduled_operation_model = ns.model('ScheduledOperation', { 'id': fields.Integer( description='Id of the scheduled operation', readonly=True, default=None), 'start_date': fields.Date( dt_format='iso8601', required=True, description='Start date of the scheduled operation'), 'stop_date': fields.Date( dt_format='iso8601', required=True, description='End date of the scheduled operation'), 'day': fields.Integer( required=True, description='Day of month for the scheduled operation'), 'frequency': fields.Integer( required=True, description='Frequency of the scheduling in months'), 'label': fields.String( required=True, description='Label of the generated operations'), 'value': fields.Float( required=True, description='Value of the generated operations'), 'category': fields.String( required=False, description='Category of the generated operations'), 'account_id': fields.Integer( default=None, readonly=True, required=True, description='Account id of the scheduled operation'), }) # Parser for an account id. account_id_parser = ns.parser() account_id_parser.add_argument( 'account_id', type=int, required=True, location='args', help='Id of the account' ) # pylint: enable=invalid-name # pylint: disable=no-self-use @ns.route('') @ns.doc( security='apikey', responses={ 401: 'Unauthorized', }) class ScheduledOperationListResource(Resource): """Resource to handle scheduled operation lists.""" @ns.expect(account_id_parser) @ns.response(200, 'OK', [scheduled_operation_model]) @ns.marshal_list_with(scheduled_operation_model) @jwt_required def get(self): """Get all scheduled operation for an account.""" data = account_id_parser.parse_args() return ScheduledOperation.query().filter_by(**data).all(), 200 @ns.expect(scheduled_operation_model) @ns.response(200, 'OK', scheduled_operation_model) @ns.response(404, 'Account not found') @ns.response(406, 'Invalid operation data') @ns.marshal_with(scheduled_operation_model) @jwt_required def post(self): """Add a new scheduled operation.""" data = self.api.payload account_id = data['account_id'] account = Account.query().get(account_id) if not account: ns.abort(404, 'Account with id %d not found.' % account_id) # A new scheduled operation MUST NOT have an id; if data.get('id') is not None: ns.abort(406, 'Id must not be provided on creation.') scheduled_operation = ScheduledOperation(**data) db.session.add(scheduled_operation) # pylint: disable=no-member scheduled_operation.reschedule() db.session.flush() # pylint: disable=no-member return scheduled_operation, 201 @ns.route('/') @ns.doc( security='apikey', params={ 'scheduled_operation_id': 'Id of the scheduled operation to manage' }, responses={ 401: 'Unauthorized', 404: 'Scheduled operation not found' }) class ScheduledOperationResource(Resource): """Resource to handle scheduled operations.""" @ns.response(200, 'OK', scheduled_operation_model) @ns.marshal_with(scheduled_operation_model) @jwt_required def get(self, scheduled_operation_id): """Get scheduled operation.""" so_id = scheduled_operation_id scheduled_operation = ScheduledOperation.query().get(so_id) if not scheduled_operation: ns.abort(404, 'Scheduled operation with id %d not found.' % so_id) return scheduled_operation, 200 @ns.response(200, 'OK', scheduled_operation_model) @ns.response(406, 'Invalid scheduled operation data') @ns.expect(scheduled_operation_model) @ns.marshal_with(scheduled_operation_model) @jwt_required def post(self, scheduled_operation_id): """Update a scheduled operation.""" data = self.api.payload so_id = scheduled_operation_id # Check ID consistency. if data.get('id', so_id) != so_id: ns.abort(406, 'Id must not be provided or changed on update.') scheduled_operation = ScheduledOperation.query().get(so_id) if not scheduled_operation: ns.abort(404, 'Scheduled operation with id %d not found.' % so_id) # FIXME check account_id consistency. # SQLAlchemy objects ignore __dict__.update() with merge. for key, value in data.items(): setattr(scheduled_operation, key, value) db.session.merge(scheduled_operation) # pylint: disable=no-member scheduled_operation.reschedule() db.session.flush() # pylint: disable=no-member return scheduled_operation, 200 @ns.response(200, 'OK', scheduled_operation_model) @ns.response(409, 'Cannot be deleted') @ns.marshal_with(scheduled_operation_model) @jwt_required def delete(self, scheduled_operation_id): """Delete a scheduled operation.""" so_id = scheduled_operation_id scheduled_operation = ScheduledOperation.query().get(so_id) if not scheduled_operation: ns.abort(404, 'Scheduled operation with id %d not found.' % so_id) operations = scheduled_operation.operations.filter( Operation.confirmed == true() ).count() if operations: ns.abort(409, 'There are still confirmed operations ' 'associated to this scheduled operation.') # Delete unconfirmed operations scheduled_operation.operations.delete() db.session.delete(scheduled_operation) # pylint: disable=no-member return None, 204