cornflow 1.1.0a1__py3-none-any.whl → 1.1.1a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cornflow/app.py +4 -0
- cornflow/cli/utils.py +1 -1
- cornflow/config.py +10 -2
- cornflow/endpoints/__init__.py +28 -19
- cornflow/endpoints/example_data.py +64 -13
- cornflow/endpoints/execution.py +1 -1
- cornflow/endpoints/login.py +26 -6
- cornflow/endpoints/reports.py +283 -0
- cornflow/migrations/versions/83164be03c23_.py +40 -0
- cornflow/migrations/versions/96f00d0961d1_reports_table.py +50 -0
- cornflow/models/__init__.py +2 -0
- cornflow/models/execution.py +8 -0
- cornflow/models/meta_models.py +23 -12
- cornflow/models/reports.py +119 -0
- cornflow/schemas/example_data.py +7 -2
- cornflow/schemas/execution.py +3 -0
- cornflow/schemas/reports.py +48 -0
- cornflow/shared/const.py +21 -0
- cornflow/shared/exceptions.py +20 -9
- cornflow/static/v1.json +3854 -0
- cornflow/tests/const.py +7 -0
- cornflow/tests/{custom_liveServer.py → custom_live_server.py} +3 -1
- cornflow/tests/custom_test_case.py +2 -3
- cornflow/tests/integration/test_commands.py +1 -1
- cornflow/tests/integration/test_cornflowclient.py +116 -28
- cornflow/tests/unit/test_alarms.py +22 -9
- cornflow/tests/unit/test_cli.py +10 -5
- cornflow/tests/unit/test_commands.py +6 -2
- cornflow/tests/unit/test_example_data.py +83 -13
- cornflow/tests/unit/test_executions.py +5 -0
- cornflow/tests/unit/test_main_alarms.py +8 -0
- cornflow/tests/unit/test_reports.py +308 -0
- cornflow/tests/unit/test_users.py +5 -2
- {cornflow-1.1.0a1.dist-info → cornflow-1.1.1a1.dist-info}/METADATA +31 -31
- {cornflow-1.1.0a1.dist-info → cornflow-1.1.1a1.dist-info}/RECORD +38 -31
- {cornflow-1.1.0a1.dist-info → cornflow-1.1.1a1.dist-info}/WHEEL +1 -1
- {cornflow-1.1.0a1.dist-info → cornflow-1.1.1a1.dist-info}/entry_points.txt +0 -0
- {cornflow-1.1.0a1.dist-info → cornflow-1.1.1a1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
"""empty message
|
2
|
+
|
3
|
+
Revision ID: 83164be03c23
|
4
|
+
Revises: 96f00d0961d1
|
5
|
+
Create Date: 2024-07-23 13:18:47.748324
|
6
|
+
|
7
|
+
"""
|
8
|
+
from alembic import op
|
9
|
+
import sqlalchemy as sa
|
10
|
+
|
11
|
+
|
12
|
+
# revision identifiers, used by Alembic.
|
13
|
+
revision = '83164be03c23'
|
14
|
+
down_revision = '96f00d0961d1'
|
15
|
+
branch_labels = None
|
16
|
+
depends_on = None
|
17
|
+
|
18
|
+
|
19
|
+
def upgrade():
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
21
|
+
with op.batch_alter_table('reports', schema=None) as batch_op:
|
22
|
+
batch_op.add_column(sa.Column('state', sa.SmallInteger(), nullable=True))
|
23
|
+
batch_op.add_column(sa.Column('state_message', sa.TEXT(), nullable=True))
|
24
|
+
batch_op.alter_column('file_url',
|
25
|
+
existing_type=sa.VARCHAR(length=256),
|
26
|
+
nullable=True)
|
27
|
+
|
28
|
+
# ### end Alembic commands ###
|
29
|
+
|
30
|
+
|
31
|
+
def downgrade():
|
32
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
33
|
+
with op.batch_alter_table('reports', schema=None) as batch_op:
|
34
|
+
batch_op.alter_column('file_url',
|
35
|
+
existing_type=sa.VARCHAR(length=256),
|
36
|
+
nullable=False)
|
37
|
+
batch_op.drop_column('state_message')
|
38
|
+
batch_op.drop_column('state')
|
39
|
+
|
40
|
+
# ### end Alembic commands ###
|
@@ -0,0 +1,50 @@
|
|
1
|
+
"""
|
2
|
+
Adds reports table to database
|
3
|
+
|
4
|
+
Revision ID: 96f00d0961d1
|
5
|
+
Revises: 991b98e24225
|
6
|
+
Create Date: 2024-06-12 18:47:06.366487
|
7
|
+
|
8
|
+
"""
|
9
|
+
|
10
|
+
from alembic import op
|
11
|
+
import sqlalchemy as sa
|
12
|
+
|
13
|
+
|
14
|
+
# revision identifiers, used by Alembic.
|
15
|
+
revision = "96f00d0961d1"
|
16
|
+
down_revision = "991b98e24225"
|
17
|
+
branch_labels = None
|
18
|
+
depends_on = None
|
19
|
+
|
20
|
+
|
21
|
+
def upgrade():
|
22
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
23
|
+
op.create_table(
|
24
|
+
"reports",
|
25
|
+
sa.Column("created_at", sa.DateTime(), nullable=False),
|
26
|
+
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
27
|
+
sa.Column("deleted_at", sa.DateTime(), nullable=True),
|
28
|
+
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
|
29
|
+
sa.Column("execution_id", sa.String(length=256), nullable=False),
|
30
|
+
sa.Column("name", sa.String(length=256), nullable=False),
|
31
|
+
sa.Column("description", sa.TEXT(), nullable=True),
|
32
|
+
sa.Column("file_url", sa.String(length=256), nullable=False),
|
33
|
+
sa.Column("user_id", sa.Integer(), nullable=False),
|
34
|
+
sa.ForeignKeyConstraint(
|
35
|
+
["execution_id"],
|
36
|
+
["executions.id"],
|
37
|
+
),
|
38
|
+
sa.ForeignKeyConstraint(
|
39
|
+
["user_id"],
|
40
|
+
["users.id"],
|
41
|
+
),
|
42
|
+
sa.PrimaryKeyConstraint("id"),
|
43
|
+
)
|
44
|
+
# ### end Alembic commands ###
|
45
|
+
|
46
|
+
|
47
|
+
def downgrade():
|
48
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
49
|
+
op.drop_table("reports")
|
50
|
+
# ### end Alembic commands ###
|
cornflow/models/__init__.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
Initialization file for the models module
|
3
3
|
"""
|
4
|
+
|
4
5
|
from .action import ActionModel
|
5
6
|
from .alarms import AlarmsModel
|
6
7
|
from .case import CaseModel
|
@@ -14,3 +15,4 @@ from .role import RoleModel
|
|
14
15
|
from .user import UserModel
|
15
16
|
from .user_role import UserRoleModel
|
16
17
|
from .view import ViewModel
|
18
|
+
from .reports import ReportModel
|
cornflow/models/execution.py
CHANGED
@@ -64,6 +64,14 @@ class ExecutionModel(BaseDataModel):
|
|
64
64
|
default=EXECUTION_STATE_MESSAGE_DICT[DEFAULT_EXECUTION_CODE],
|
65
65
|
nullable=True,
|
66
66
|
)
|
67
|
+
reports = db.relationship(
|
68
|
+
"ReportModel",
|
69
|
+
backref="executions",
|
70
|
+
lazy=True,
|
71
|
+
primaryjoin="and_(ExecutionModel.id==ReportModel.execution_id, "
|
72
|
+
"ReportModel.deleted_at==None)",
|
73
|
+
cascade="all,delete",
|
74
|
+
)
|
67
75
|
|
68
76
|
def __init__(self, data):
|
69
77
|
super().__init__(data)
|
cornflow/models/meta_models.py
CHANGED
@@ -33,17 +33,29 @@ class EmptyBaseModel(db.Model):
|
|
33
33
|
|
34
34
|
try:
|
35
35
|
db.session.commit()
|
36
|
-
current_app.logger.debug(
|
36
|
+
current_app.logger.debug(
|
37
|
+
f"Transaction type: {action}, performed correctly on {self}"
|
38
|
+
)
|
39
|
+
|
37
40
|
except IntegrityError as err:
|
38
41
|
db.session.rollback()
|
39
42
|
current_app.logger.error(f"Integrity error on {action} data: {err}")
|
40
43
|
current_app.logger.error(f"Data: {self.__dict__}")
|
41
|
-
|
44
|
+
|
45
|
+
if "FOREIGN KEY" in str(err):
|
46
|
+
message = f"Foreign key constraint error while {action} on {self.__class__.__tablename__} table"
|
47
|
+
raise InvalidData(message)
|
48
|
+
else:
|
49
|
+
raise InvalidData(f"Integrity error on {action} with data {self}")
|
50
|
+
|
42
51
|
except DBAPIError as err:
|
43
52
|
db.session.rollback()
|
44
|
-
current_app.logger.error(
|
53
|
+
current_app.logger.error(
|
54
|
+
f"Unknown database error on {action} data: {type(err)}"
|
55
|
+
)
|
45
56
|
current_app.logger.error(f"Data: {self.__dict__}")
|
46
57
|
raise InvalidData(f"Unknown database error on {action} with data {self}")
|
58
|
+
|
47
59
|
except Exception as err:
|
48
60
|
db.session.rollback()
|
49
61
|
current_app.logger.error(f"Unknown error on {action} data: {err}")
|
@@ -99,7 +111,9 @@ class EmptyBaseModel(db.Model):
|
|
99
111
|
action = "bulk create"
|
100
112
|
try:
|
101
113
|
db.session.commit()
|
102
|
-
current_app.logger.debug(
|
114
|
+
current_app.logger.debug(
|
115
|
+
f"Transaction type: {action}, performed correctly on {cls}"
|
116
|
+
)
|
103
117
|
except IntegrityError as err:
|
104
118
|
db.session.rollback()
|
105
119
|
current_app.logger.error(f"Integrity error on {action} data: {err}")
|
@@ -120,7 +134,9 @@ class EmptyBaseModel(db.Model):
|
|
120
134
|
action = "bulk create update"
|
121
135
|
try:
|
122
136
|
db.session.commit()
|
123
|
-
current_app.logger.debug(
|
137
|
+
current_app.logger.debug(
|
138
|
+
f"Transaction type: {action}, performed correctly on {cls}"
|
139
|
+
)
|
124
140
|
except IntegrityError as err:
|
125
141
|
db.session.rollback()
|
126
142
|
current_app.logger.error(f"Integrity error on {action} data: {err}")
|
@@ -136,12 +152,7 @@ class EmptyBaseModel(db.Model):
|
|
136
152
|
return instances
|
137
153
|
|
138
154
|
@classmethod
|
139
|
-
def get_all_objects(
|
140
|
-
cls,
|
141
|
-
offset=0,
|
142
|
-
limit=None,
|
143
|
-
**kwargs
|
144
|
-
):
|
155
|
+
def get_all_objects(cls, offset=0, limit=None, **kwargs):
|
145
156
|
"""
|
146
157
|
Method to get all the objects from the database applying the filters passed as keyword arguments
|
147
158
|
|
@@ -261,7 +272,7 @@ class TraceAttributesModel(EmptyBaseModel):
|
|
261
272
|
update_date_lte=None,
|
262
273
|
offset=0,
|
263
274
|
limit=None,
|
264
|
-
**kwargs
|
275
|
+
**kwargs,
|
265
276
|
):
|
266
277
|
"""
|
267
278
|
Method to get all the objects from the database applying the filters passed as keyword arguments
|
@@ -0,0 +1,119 @@
|
|
1
|
+
"""
|
2
|
+
Model for the reports
|
3
|
+
"""
|
4
|
+
|
5
|
+
# Import from libraries
|
6
|
+
from sqlalchemy.dialects.postgresql import TEXT
|
7
|
+
from sqlalchemy.ext.declarative import declared_attr
|
8
|
+
|
9
|
+
# Imports from internal modules
|
10
|
+
from cornflow.models.base_data_model import TraceAttributesModel
|
11
|
+
from cornflow.shared import db
|
12
|
+
from cornflow.shared.const import REPORT_STATE, REPORT_STATE_MSG
|
13
|
+
|
14
|
+
|
15
|
+
class ReportModel(TraceAttributesModel):
|
16
|
+
"""
|
17
|
+
Model class for the Reports.
|
18
|
+
It inherits from :class:`TraceAttributesModel<cornflow.models.base_data_model.TraceAttributesModel>` to have the trace fields and user field.
|
19
|
+
|
20
|
+
- **id**: int, the report id, primary key for the reports.
|
21
|
+
- **execution_id**: str, the foreign key for the execution (:class:`ExecutionModel`). It links the report to its
|
22
|
+
parent execution.
|
23
|
+
- **file_url**: str, the link with the actual report. It should be a valid url to a cloud storage bucket or a file.
|
24
|
+
- **name**: str, the name of the report given by the user.
|
25
|
+
- **description**: str, the description of the report given by the user. It is optional.
|
26
|
+
- **user_id**: int, the foreign key for the user (:class:`UserModel`). It links the report to its owner.
|
27
|
+
- **created_at**: datetime, the datetime when the report was created (in UTC).
|
28
|
+
This datetime is generated automatically, the user does not need to provide it.
|
29
|
+
- **updated_at**: datetime, the datetime when the report was last updated (in UTC).
|
30
|
+
This datetime is generated automatically, the user does not need to provide it.
|
31
|
+
- **deleted_at**: datetime, the datetime when the report was deleted (in UTC). Even though it is deleted,
|
32
|
+
actually, it is not deleted from the database, in order to have a command that cleans up deleted data
|
33
|
+
after a certain time of its deletion.
|
34
|
+
This datetime is generated automatically, the user does not need to provide it.
|
35
|
+
|
36
|
+
"""
|
37
|
+
|
38
|
+
# Table name in the database
|
39
|
+
__tablename__ = "reports"
|
40
|
+
|
41
|
+
# Model fields
|
42
|
+
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
43
|
+
execution_id = db.Column(
|
44
|
+
db.String(256), db.ForeignKey("executions.id"), nullable=False
|
45
|
+
)
|
46
|
+
name = db.Column(db.String(256), nullable=False)
|
47
|
+
description = db.Column(TEXT, nullable=True)
|
48
|
+
file_url = db.Column(db.String(256), nullable=True)
|
49
|
+
state = db.Column(db.SmallInteger, default=REPORT_STATE.CORRECT)
|
50
|
+
state_message = db.Column(TEXT, default=REPORT_STATE_MSG[REPORT_STATE.CORRECT])
|
51
|
+
|
52
|
+
@declared_attr
|
53
|
+
def user_id(self):
|
54
|
+
"""
|
55
|
+
The foreign key for the user (:class:`UserModel<cornflow.models.UserModel>`).
|
56
|
+
"""
|
57
|
+
return db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
|
58
|
+
|
59
|
+
@declared_attr
|
60
|
+
def user(self):
|
61
|
+
return db.relationship("UserModel")
|
62
|
+
|
63
|
+
def __init__(self, data: dict):
|
64
|
+
super().__init__()
|
65
|
+
self.user_id = data.get("user_id")
|
66
|
+
self.execution_id = data.get("execution_id")
|
67
|
+
self.name = data.get("name")
|
68
|
+
self.description = data.get("description")
|
69
|
+
self.file_url = data.get("file_url")
|
70
|
+
self.state = data.get("state")
|
71
|
+
if self.state is None:
|
72
|
+
if self.file_url is None:
|
73
|
+
self.state = REPORT_STATE.UNKNOWN
|
74
|
+
else:
|
75
|
+
self.state = REPORT_STATE.CORRECT
|
76
|
+
self.state_message = data.get("state_message")
|
77
|
+
if self.state_message is None:
|
78
|
+
self.state_message = REPORT_STATE_MSG.get(self.state)
|
79
|
+
|
80
|
+
def update(self, data):
|
81
|
+
"""
|
82
|
+
Method used to update a report from the database
|
83
|
+
|
84
|
+
:param dict data: the data of the object
|
85
|
+
:return: None
|
86
|
+
:rtype: None
|
87
|
+
"""
|
88
|
+
# we try to keep the state_message synced, by default
|
89
|
+
if "state" in data and "state_message" not in data:
|
90
|
+
data["state_message"] = REPORT_STATE_MSG[data["state"]]
|
91
|
+
super().update(data)
|
92
|
+
|
93
|
+
def update_link(self, file_url: str):
|
94
|
+
"""
|
95
|
+
Method to update the report link
|
96
|
+
|
97
|
+
:param str file_url: new URL for the report
|
98
|
+
:return: nothing
|
99
|
+
"""
|
100
|
+
self.file_url = file_url
|
101
|
+
super().update({})
|
102
|
+
|
103
|
+
def __repr__(self):
|
104
|
+
"""
|
105
|
+
Method to represent the class :class:`ReportModel`
|
106
|
+
|
107
|
+
:return: The representation of the :class:`ReportModel`
|
108
|
+
:rtype: str
|
109
|
+
"""
|
110
|
+
return f"<Report {self.id}>"
|
111
|
+
|
112
|
+
def __str__(self):
|
113
|
+
"""
|
114
|
+
Method to print a string representation of the :class:`ReportModel`
|
115
|
+
|
116
|
+
:return: The string for the :class:`ReportModel`
|
117
|
+
:rtype: str
|
118
|
+
"""
|
119
|
+
return self.__repr__()
|
cornflow/schemas/example_data.py
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
from marshmallow import fields, Schema
|
2
2
|
|
3
3
|
|
4
|
-
class
|
4
|
+
class ExampleListData(Schema):
|
5
5
|
name = fields.Str(required=True)
|
6
|
-
|
6
|
+
description = fields.Str(required=False)
|
7
|
+
|
8
|
+
|
9
|
+
class ExampleDetailData(ExampleListData):
|
10
|
+
instance = fields.Raw(required=True)
|
11
|
+
solution = fields.Raw(required=False)
|
cornflow/schemas/execution.py
CHANGED
@@ -5,6 +5,7 @@ from marshmallow import fields, Schema, validate
|
|
5
5
|
from cornflow.shared.const import MIN_EXECUTION_STATUS_CODE, MAX_EXECUTION_STATUS_CODE
|
6
6
|
from .common import QueryFilters, BaseDataEndpointResponse
|
7
7
|
from .solution_log import LogSchema, BasicLogSchema
|
8
|
+
from .reports import ReportSchemaBase
|
8
9
|
|
9
10
|
|
10
11
|
class QueryFiltersExecution(QueryFilters):
|
@@ -30,6 +31,7 @@ class ConfigSchema(Schema):
|
|
30
31
|
threads = fields.Int(required=False)
|
31
32
|
logPath = fields.Str(required=False)
|
32
33
|
MIPGap = fields.Float(required=False)
|
34
|
+
report = fields.Raw(required=False)
|
33
35
|
|
34
36
|
|
35
37
|
class ConfigSchemaResponse(ConfigSchema):
|
@@ -95,6 +97,7 @@ class ExecutionDagPostRequest(ExecutionRequest, ExecutionDagRequest):
|
|
95
97
|
|
96
98
|
|
97
99
|
class ExecutionDetailsEndpointResponse(BaseDataEndpointResponse):
|
100
|
+
reports = fields.Nested(ReportSchemaBase, many=True)
|
98
101
|
config = fields.Nested(ConfigSchemaResponse)
|
99
102
|
instance_id = fields.Str()
|
100
103
|
state = fields.Int()
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Imports from libraries
|
2
|
+
from marshmallow import fields, Schema, INCLUDE
|
3
|
+
|
4
|
+
# Imports from internal modules
|
5
|
+
from .common import BaseQueryFilters
|
6
|
+
|
7
|
+
|
8
|
+
class QueryFiltersReports(BaseQueryFilters):
|
9
|
+
execution_id = fields.Str(required=False)
|
10
|
+
|
11
|
+
|
12
|
+
class ReportSchemaBase(Schema):
|
13
|
+
id = fields.Int(dump_only=True)
|
14
|
+
file_url = fields.Str(required=False)
|
15
|
+
name = fields.Str(required=True)
|
16
|
+
state = fields.Int()
|
17
|
+
|
18
|
+
|
19
|
+
class ReportSchema(ReportSchemaBase):
|
20
|
+
user_id = fields.Int(required=False, load_only=True)
|
21
|
+
execution_id = fields.Str(required=True)
|
22
|
+
description = fields.Str()
|
23
|
+
state_message = fields.Str()
|
24
|
+
created_at = fields.DateTime(dump_only=True)
|
25
|
+
updated_at = fields.DateTime(dump_only=True)
|
26
|
+
deleted_at = fields.DateTime(dump_only=True)
|
27
|
+
|
28
|
+
|
29
|
+
class ReportEditRequest(Schema):
|
30
|
+
class META:
|
31
|
+
unknown = INCLUDE
|
32
|
+
|
33
|
+
name = fields.Str()
|
34
|
+
description = fields.Str()
|
35
|
+
file_url = fields.Str(required=False)
|
36
|
+
state = fields.Int()
|
37
|
+
state_message = fields.Str()
|
38
|
+
|
39
|
+
|
40
|
+
class ReportRequest(Schema):
|
41
|
+
class META:
|
42
|
+
unknown = INCLUDE
|
43
|
+
|
44
|
+
name = fields.Str(required=True)
|
45
|
+
description = fields.Str(required=False)
|
46
|
+
execution_id = fields.Str(required=True)
|
47
|
+
state = fields.Int()
|
48
|
+
state_message = fields.Str()
|
cornflow/shared/const.py
CHANGED
@@ -43,6 +43,25 @@ AIRFLOW_TO_STATE_MAP = dict(
|
|
43
43
|
queued=EXEC_STATE_QUEUED,
|
44
44
|
)
|
45
45
|
|
46
|
+
# Reports codes
|
47
|
+
|
48
|
+
|
49
|
+
class REPORT_STATE:
|
50
|
+
RUNNING = 0
|
51
|
+
CORRECT = 1
|
52
|
+
ERROR = -1
|
53
|
+
UNKNOWN = -5
|
54
|
+
ERROR_NO_QUARTO = -10
|
55
|
+
|
56
|
+
|
57
|
+
REPORT_STATE_MSG = {
|
58
|
+
REPORT_STATE.RUNNING: "The report is currently running.",
|
59
|
+
REPORT_STATE.CORRECT: "The report has been solved correctly.",
|
60
|
+
REPORT_STATE.ERROR: "The report has an error.",
|
61
|
+
REPORT_STATE.UNKNOWN: "The report has an unknown error.",
|
62
|
+
REPORT_STATE.ERROR_NO_QUARTO: "The report failed because Quarto was not found.",
|
63
|
+
}
|
64
|
+
|
46
65
|
# These codes and names are inherited from flask app builder in order to have the same names and values
|
47
66
|
# as this library that is the base of airflow
|
48
67
|
AUTH_DB = 1
|
@@ -122,4 +141,6 @@ BASE_PERMISSION_ASSIGNATION = [
|
|
122
141
|
|
123
142
|
EXTRA_PERMISSION_ASSIGNATION = [
|
124
143
|
(VIEWER_ROLE, PUT_ACTION, "user-detail"),
|
144
|
+
(VIEWER_ROLE, GET_ACTION, "report"),
|
145
|
+
(PLANNER_ROLE, GET_ACTION, "report"),
|
125
146
|
]
|
cornflow/shared/exceptions.py
CHANGED
@@ -21,7 +21,10 @@ class InvalidUsage(Exception):
|
|
21
21
|
def __init__(self, error=None, status_code=None, payload=None, log_txt=None):
|
22
22
|
Exception.__init__(self, error)
|
23
23
|
if error is not None:
|
24
|
-
|
24
|
+
if isinstance(error, Exception):
|
25
|
+
self.error = str(error)
|
26
|
+
else:
|
27
|
+
self.error = error
|
25
28
|
if status_code is not None:
|
26
29
|
self.status_code = status_code
|
27
30
|
self.payload = payload
|
@@ -122,10 +125,21 @@ class ConfigurationError(InvalidUsage):
|
|
122
125
|
error = "No authentication method configured on the server"
|
123
126
|
|
124
127
|
|
128
|
+
class FileError(InvalidUsage):
|
129
|
+
"""
|
130
|
+
Exception used when there is an error regarding the upload of a file to the server
|
131
|
+
"""
|
132
|
+
|
133
|
+
status_code = 400
|
134
|
+
error = "Error uploading the file"
|
135
|
+
|
136
|
+
|
125
137
|
INTERNAL_SERVER_ERROR_MESSAGE = "500 Internal Server Error"
|
126
|
-
INTERNAL_SERVER_ERROR_MESSAGE_DETAIL =
|
127
|
-
|
128
|
-
|
138
|
+
INTERNAL_SERVER_ERROR_MESSAGE_DETAIL = (
|
139
|
+
"The server encountered an internal error and was unable "
|
140
|
+
"to complete your request. Either the server is overloaded or "
|
141
|
+
"there is an error in the application."
|
142
|
+
)
|
129
143
|
|
130
144
|
|
131
145
|
def initialize_errorhandlers(app):
|
@@ -187,10 +201,7 @@ def initialize_errorhandlers(app):
|
|
187
201
|
status_code = error.code or status_code
|
188
202
|
error_msg = f"{status_code} {error.name or INTERNAL_SERVER_ERROR_MESSAGE}"
|
189
203
|
error_str = f"{error_msg}. {str(error.description or '') or INTERNAL_SERVER_ERROR_MESSAGE_DETAIL}"
|
190
|
-
response_dict = {
|
191
|
-
"message": error_msg,
|
192
|
-
"error": error_str
|
193
|
-
}
|
204
|
+
response_dict = {"message": error_msg, "error": error_str}
|
194
205
|
response = jsonify(response_dict)
|
195
206
|
|
196
207
|
elif app.config["ENV"] == "production":
|
@@ -202,7 +213,7 @@ def initialize_errorhandlers(app):
|
|
202
213
|
|
203
214
|
response_dict = {
|
204
215
|
"message": INTERNAL_SERVER_ERROR_MESSAGE,
|
205
|
-
"error": INTERNAL_SERVER_ERROR_MESSAGE_DETAIL
|
216
|
+
"error": INTERNAL_SERVER_ERROR_MESSAGE_DETAIL,
|
206
217
|
}
|
207
218
|
response = jsonify(response_dict)
|
208
219
|
else:
|