233 lines
6.7 KiB
Python
233 lines
6.7 KiB
Python
"""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.DateTime(
|
|
dt_format='iso8601',
|
|
required=True,
|
|
description='Start date of the scheduled operation'),
|
|
'stop_date': fields.DateTime(
|
|
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,
|
|
error_message='Account with id %d not found.' % account_id
|
|
)
|
|
|
|
# A new scheduled operation MUST NOT have an id;
|
|
if 'id' in data and data['id']:
|
|
ns.abort(
|
|
406,
|
|
error_message='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('/<int:scheduled_operation_id>')
|
|
@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,
|
|
error_message='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 'id' in data and data['id'] and data['id'] != so_id:
|
|
ns.abort(
|
|
406,
|
|
error_message='Id must not be provided or changed on update.'
|
|
)
|
|
|
|
scheduled_operation = ScheduledOperation.query().get(so_id)
|
|
|
|
if not scheduled_operation:
|
|
ns.abort(
|
|
404,
|
|
error_message='Scheduled operation with id %d not found.' % so_id
|
|
)
|
|
|
|
# FIXME check account_id consistency.
|
|
|
|
# SQLAlchemy objects ignore __dict__.update() with merge.
|
|
for k, v in data.items():
|
|
setattr(scheduled_operation, k, v)
|
|
|
|
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,
|
|
error_message='Scheduled operation with id %d not found.' % so_id
|
|
)
|
|
|
|
operations = scheduled_operation.operations.filter(
|
|
Operation.confirmed == true()
|
|
).count()
|
|
|
|
if operations:
|
|
ns.abort(
|
|
409,
|
|
error_message='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
|