Coverage for yaptide/routes/user_routes.py: 91%
65 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-22 07:31 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-22 07:31 +0000
1import logging
2from enum import Enum
4from flask import request
5from flask_restful import Resource
6from marshmallow import Schema, fields
7from sqlalchemy import asc, desc
9from yaptide.persistence.models import SimulationModel, UserModel
10from yaptide.routes.utils.decorators import requires_auth
11from yaptide.routes.utils.response_templates import (error_validation_response, yaptide_response)
12from yaptide.persistence.db_methods import (delete_object_from_db, fetch_simulation_by_job_id)
13from yaptide.utils.enums import EntityState
15DEFAULT_PAGE_SIZE = 6 # default number of simulations per page
16DEFAULT_PAGE_IDX = 1 # default page index
19class OrderType(Enum):
20 """Order type"""
22 ASCEND = "ascend"
23 DESCEND = "descend"
26class OrderBy(Enum):
27 """Order by column"""
29 START_TIME = "start_time"
30 END_TIME = "end_time"
33class UserSimulations(Resource):
34 """Class responsible for returning user's simulations' basic infos"""
36 class GetAPIParametersSchema(Schema):
37 """Class specifies Get API parameters"""
39 page_size = fields.Integer(load_default=DEFAULT_PAGE_SIZE)
40 page_idx = fields.Integer(load_default=DEFAULT_PAGE_IDX)
41 order_by = fields.String(load_default=OrderBy.START_TIME.value)
42 order_type = fields.String(load_default=OrderType.DESCEND.value)
44 class DeleteAPIParametersSchema(Schema):
45 """Schema for DELETE method parameters"""
47 job_id = fields.String(required=True) # job_id is mandatory for DELETE
49 @staticmethod
50 @requires_auth()
51 def get(user: UserModel):
52 """Method returning simulations from the database"""
53 schema = UserSimulations.GetAPIParametersSchema()
54 params_dict: dict = schema.load(request.args)
55 logging.info('User %s requested simulations with parameters: %s', user.username, params_dict)
57 # Query the database for the paginated results
58 sorting = desc if params_dict['order_type'] == OrderType.DESCEND.value else asc
59 query = SimulationModel.query.\
60 filter(SimulationModel.job_id != None).\
61 filter_by(user_id=user.id).\
62 order_by(sorting(params_dict['order_by']))
63 pagination = query.paginate(page=params_dict['page_idx'], per_page=params_dict['page_size'], error_out=False)
64 simulations = pagination.items
66 result = {
67 'simulations': [
68 {
69 'title': simulation.title,
70 'job_id': simulation.job_id,
71 'start_time': simulation.start_time,
72 # submission time, when user send the request to the backend - jobs may start much later than that
73 'end_time': simulation.end_time,
74 # end time, when the all jobs are finished and results are merged
75 'metadata': {
76 'platform': simulation.platform,
77 'server': 'Yaptide',
78 'input_type': simulation.input_type,
79 'sim_type': simulation.sim_type
80 }
81 } for simulation in simulations
82 ],
83 'page_count':
84 pagination.pages,
85 'simulations_count':
86 pagination.total,
87 }
88 return yaptide_response(message='User Simulations', code=200, content=result)
90 @staticmethod
91 @requires_auth()
92 def delete(user: UserModel):
93 """Method deleting simulation from database"""
94 schema = UserSimulations.DeleteAPIParametersSchema()
95 errors: dict[str, list[str]] = schema.validate(request.args)
96 if errors:
97 return error_validation_response(content=errors)
98 params_dict: dict = schema.load(request.args)
100 job_id = params_dict['job_id']
101 simulation = fetch_simulation_by_job_id(job_id)
103 if simulation is None:
104 return yaptide_response(message=f'Simulation with job_id={job_id} do not exist', code=404)
106 if simulation.user_id != user.id:
107 return yaptide_response(message='Unauthorized: You do not have permission to delete this simulation',
108 code=401)
110 # Simulation has to be completed/cancelled before deleting it.
111 if simulation.job_state in (EntityState.UNKNOWN.value, EntityState.PENDING.value, EntityState.RUNNING.value):
112 return yaptide_response(message=f'''Simulation with job_id={job_id} is currently running.
113 Please cancel simulation or wait for it to finish''',
114 code=403)
116 delete_object_from_db(simulation)
117 return yaptide_response(message=f'Simulation with job_id={job_id} successfully deleted from database', code=200)
120class UserUpdate(Resource):
121 """Class responsible for updating the user"""
123 @staticmethod
124 @requires_auth()
125 def post(user: UserModel):
126 """Updates user with provided parameters"""
127 json_data: dict = request.get_json(force=True)
128 if not json_data:
129 return error_validation_response()
130 return yaptide_response(message=f'User {user.username} updated', code=202)