cornflow 1.1.1a1__py3-none-any.whl → 1.1.2__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.
Files changed (35) hide show
  1. cornflow/app.py +0 -4
  2. cornflow/cli/utils.py +1 -1
  3. cornflow/config.py +2 -10
  4. cornflow/endpoints/__init__.py +0 -14
  5. cornflow/endpoints/execution.py +1 -1
  6. cornflow/endpoints/login.py +6 -26
  7. cornflow/models/__init__.py +0 -2
  8. cornflow/models/execution.py +0 -8
  9. cornflow/models/meta_models.py +12 -23
  10. cornflow/schemas/execution.py +0 -3
  11. cornflow/shared/const.py +0 -21
  12. cornflow/shared/exceptions.py +9 -20
  13. cornflow/tests/const.py +0 -7
  14. cornflow/tests/{custom_live_server.py → custom_liveServer.py} +1 -3
  15. cornflow/tests/custom_test_case.py +3 -2
  16. cornflow/tests/integration/test_commands.py +1 -1
  17. cornflow/tests/integration/test_cornflowclient.py +28 -116
  18. cornflow/tests/unit/test_alarms.py +9 -22
  19. cornflow/tests/unit/test_cli.py +5 -10
  20. cornflow/tests/unit/test_commands.py +2 -6
  21. cornflow/tests/unit/test_executions.py +0 -5
  22. cornflow/tests/unit/test_main_alarms.py +0 -8
  23. cornflow/tests/unit/test_users.py +2 -5
  24. {cornflow-1.1.1a1.dist-info → cornflow-1.1.2.dist-info}/METADATA +7 -7
  25. {cornflow-1.1.1a1.dist-info → cornflow-1.1.2.dist-info}/RECORD +28 -35
  26. cornflow/endpoints/reports.py +0 -283
  27. cornflow/migrations/versions/83164be03c23_.py +0 -40
  28. cornflow/migrations/versions/96f00d0961d1_reports_table.py +0 -50
  29. cornflow/models/reports.py +0 -119
  30. cornflow/schemas/reports.py +0 -48
  31. cornflow/static/v1.json +0 -3854
  32. cornflow/tests/unit/test_reports.py +0 -308
  33. {cornflow-1.1.1a1.dist-info → cornflow-1.1.2.dist-info}/WHEEL +0 -0
  34. {cornflow-1.1.1a1.dist-info → cornflow-1.1.2.dist-info}/entry_points.txt +0 -0
  35. {cornflow-1.1.1a1.dist-info → cornflow-1.1.2.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,17 @@
1
1
  """
2
- Main script to run the integration tests of cornflow-server
3
- """
4
2
 
3
+ """
4
+ # Full imports
5
5
  import json
6
+ import pulp
6
7
  import logging as log
7
- import os
8
8
  import time
9
- from typing import Callable, Any
10
9
 
11
- import pulp
10
+ # Imports from environment
12
11
  from cornflow_client import CornFlowApiError
13
12
  from cornflow_client.constants import INSTANCE_SCHEMA, SOLUTION_SCHEMA
14
13
 
14
+ # Import internal modules
15
15
  from cornflow.app import create_app
16
16
  from cornflow.shared.const import (
17
17
  EXEC_STATE_CORRECT,
@@ -19,13 +19,12 @@ from cornflow.shared.const import (
19
19
  EXEC_STATE_RUNNING,
20
20
  EXEC_STATE_QUEUED,
21
21
  STATUS_HEALTHY,
22
- REPORT_STATE,
23
22
  )
24
- from cornflow.tests.const import INSTANCE_PATH, CASE_PATH, INSTANCE_MPS, INSTANCE_TSP
25
- from cornflow.tests.custom_live_server import CustomTestCaseLive
23
+ from cornflow.tests.const import INSTANCE_PATH, CASE_PATH
24
+ from cornflow.tests.custom_liveServer import CustomTestCaseLive
26
25
 
27
26
 
28
- def load_file(_file: str):
27
+ def load_file(_file):
29
28
  with open(_file) as f:
30
29
  temp = json.load(f)
31
30
  return temp
@@ -35,7 +34,6 @@ class TestCornflowClientBasic(CustomTestCaseLive):
35
34
  def setUp(self, create_all=False):
36
35
  super().setUp()
37
36
  self.items_to_check = ["name", "description"]
38
- log.info(f"Start test case name: {self.id()}")
39
37
 
40
38
  def check_status_evolution(self, execution, end_state=EXEC_STATE_CORRECT):
41
39
  statuses = [execution["state"]]
@@ -129,7 +127,7 @@ class TestCornflowClientBasic(CustomTestCaseLive):
129
127
  return execution
130
128
 
131
129
  def create_instance_and_execution(self):
132
- one_instance = self.create_new_instance(INSTANCE_MPS)
130
+ one_instance = self.create_new_instance("./cornflow/tests/data/test_mps.mps")
133
131
  name = "test_execution_name_123"
134
132
  description = "test_execution_description_123"
135
133
  schema = "solve_model_dag"
@@ -142,31 +140,6 @@ class TestCornflowClientBasic(CustomTestCaseLive):
142
140
  )
143
141
  return self.create_new_execution(payload)
144
142
 
145
- def create_instance_and_execution_report(
146
- self,
147
- schema="tsp",
148
- solver="cpsat",
149
- data=None,
150
- timeLimit=10,
151
- report_name="report",
152
- ):
153
- name = "test_instance_1"
154
- description = "description123"
155
- if data is None:
156
- data = load_file(INSTANCE_TSP)
157
- payload = dict(data=data, name=name, description=description, schema=schema)
158
- one_instance = self.create_new_instance_payload(payload)
159
- payload = dict(
160
- instance_id=one_instance["id"],
161
- config=dict(
162
- solver=solver, timeLimit=timeLimit, report=dict(name=report_name)
163
- ),
164
- description="test_execution_description_123",
165
- name="test_execution_123",
166
- schema=schema,
167
- )
168
- return self.create_new_execution(payload)
169
-
170
143
  def create_timer_instance_and_execution(self, seconds=5):
171
144
  payload = dict(
172
145
  data=dict(seconds=seconds),
@@ -184,47 +157,16 @@ class TestCornflowClientBasic(CustomTestCaseLive):
184
157
  )
185
158
  return self.create_new_execution(payload)
186
159
 
187
- @staticmethod
188
- def try_until_condition(
189
- func: Callable,
190
- condition: Callable[[Any], bool],
191
- number_of_times: int = 10,
192
- sleep_time: float = 10,
193
- ):
194
- for i in range(number_of_times):
195
- time.sleep(sleep_time)
196
- result = func()
197
- if condition(result):
198
- return result
199
- raise TimeoutError(
200
- "Timed out after {} seconds".format(number_of_times * sleep_time)
201
- )
202
-
203
- @staticmethod
204
- def wait_until_report_finishes(
205
- client, execution_id: str, report_status=REPORT_STATE.CORRECT
206
- ):
207
- def func():
208
- my_reports = client.raw.get_results(execution_id).json()["reports"]
209
- if len(my_reports) == 0:
210
- return None
211
- first = my_reports[0]
212
- if first["state"] != report_status:
213
- return None
214
- return first
215
-
216
- return func
217
-
218
160
 
219
161
  class TestCornflowClientOpen(TestCornflowClientBasic):
220
162
  # TODO: user management
221
163
  # TODO: infeasible execution
222
164
 
223
165
  def test_new_instance_file(self):
224
- self.create_new_instance_file(INSTANCE_MPS)
166
+ self.create_new_instance_file("./cornflow/tests/data/test_mps.mps")
225
167
 
226
168
  def test_new_instance(self):
227
- return self.create_new_instance(INSTANCE_MPS)
169
+ return self.create_new_instance("./cornflow/tests/data/test_mps.mps")
228
170
 
229
171
  # TODO: reactivate test with new version of cornflow client which allows to pass
230
172
  # optional arguments for the headers of the request
@@ -247,49 +189,6 @@ class TestCornflowClientOpen(TestCornflowClientBasic):
247
189
  def test_new_execution(self):
248
190
  return self.create_instance_and_execution()
249
191
 
250
- def test_new_execution_with_tsp_report(self):
251
- return self.create_instance_and_execution_report()
252
-
253
- #
254
- def test_new_execution_with_tsp_report_wait(self):
255
- execution = self.create_instance_and_execution_report()
256
- func = self.wait_until_report_finishes(self.client, execution["id"])
257
- reports_info = self.try_until_condition(func, lambda v: v is not None, 20, 5)
258
- id_report = reports_info["id"]
259
- my_name = "./my_report.html"
260
- try:
261
- os.remove(my_name)
262
- except:
263
- pass
264
- self.client.raw.get_one_report(id_report, "./", my_name)
265
- self.assertTrue(os.path.exists(my_name))
266
- try:
267
- os.remove(my_name)
268
- except OSError:
269
- pass
270
-
271
- # read header of file? we can parse it with beatifulsoup
272
-
273
- def test_new_execution_with_timer_report_wait(self):
274
- payload = dict(
275
- solver="default", schema="timer", data={"seconds": 1}, timeLimit=1
276
- )
277
- execution = self.create_instance_and_execution_report(**payload)
278
- func = self.wait_until_report_finishes(self.client, execution["id"])
279
- reports_info = self.try_until_condition(func, lambda v: v is not None, 20, 5)
280
- id_report = reports_info["id"]
281
- my_name = "./my_report.html"
282
- try:
283
- os.remove(my_name)
284
- except:
285
- pass
286
- self.client.raw.get_one_report(id_report, "./", my_name)
287
- self.assertTrue(os.path.exists(my_name))
288
- try:
289
- os.remove(my_name)
290
- except OSError:
291
- pass
292
-
293
192
  def test_delete_execution(self):
294
193
  execution = self.test_new_execution()
295
194
  response = self.client.raw.get_api_for_id("execution/", execution["id"])
@@ -314,7 +213,7 @@ class TestCornflowClientOpen(TestCornflowClientBasic):
314
213
  self.assertTrue("error" in response.json())
315
214
 
316
215
  def test_new_execution_bad_dag_name(self):
317
- one_instance = self.create_new_instance(INSTANCE_MPS)
216
+ one_instance = self.create_new_instance("./cornflow/tests/data/test_mps.mps")
318
217
  name = "test_execution_name_123"
319
218
  description = "test_execution_description_123"
320
219
  payload = dict(
@@ -328,7 +227,7 @@ class TestCornflowClientOpen(TestCornflowClientBasic):
328
227
  self.assertRaises(CornFlowApiError, _bad_func)
329
228
 
330
229
  def test_new_execution_with_schema(self):
331
- one_instance = self.create_new_instance(INSTANCE_MPS)
230
+ one_instance = self.create_new_instance("./cornflow/tests/data/test_mps.mps")
332
231
  name = "test_execution_name_123"
333
232
  description = "test_execution_description_123"
334
233
  payload = dict(
@@ -410,6 +309,19 @@ class TestCornflowClientAdmin(TestCornflowClientBasic):
410
309
  def setUp(self, create_all=False):
411
310
  super().setUp()
412
311
 
312
+ # we create a service user:
313
+ self.create_service_user(
314
+ dict(username="airflow", pwd="Airflow_test_password1", email="af@cf.com")
315
+ )
316
+
317
+ self.create_service_user(
318
+ dict(
319
+ username="service_user@cornflow.com",
320
+ pwd="Serviceuser_1234",
321
+ email="service_user@cornflow.com",
322
+ )
323
+ )
324
+
413
325
  # we create an admin user
414
326
  # we guarantee that the admin is there for airflow
415
327
  self.client.token = self.create_admin(
@@ -492,7 +404,7 @@ class TestCornflowClientAdmin(TestCornflowClientBasic):
492
404
  self.assertIsNone(execution_data["data"])
493
405
 
494
406
  def test_edit_one_execution(self):
495
- one_instance = self.create_new_instance(INSTANCE_MPS)
407
+ one_instance = self.create_new_instance("./cornflow/tests/data/test_mps.mps")
496
408
  payload = dict(
497
409
  name="bla",
498
410
  config=dict(solver="CBC"),
@@ -543,7 +455,7 @@ class TestCornflowClientAdmin(TestCornflowClientBasic):
543
455
  self.assertRaises(CornFlowApiError, _launch_too_soon_func)
544
456
 
545
457
  def test_check_instance(self):
546
- instance = self.create_new_instance(INSTANCE_MPS)
458
+ instance = self.create_new_instance("./cornflow/tests/data/test_mps.mps")
547
459
  data_check_execution = self.client.create_instance_data_check(instance["id"])
548
460
  self.assertEqual(data_check_execution["instance_id"], instance["id"])
549
461
  status = self.client.get_status(data_check_execution["id"])
@@ -1,10 +1,6 @@
1
1
  """
2
2
 
3
3
  """
4
-
5
- import unittest
6
- import os
7
-
8
4
  # Imports from internal modules
9
5
  from cornflow.models import AlarmsModel
10
6
  from cornflow.tests.const import ALARMS_URL
@@ -19,34 +15,25 @@ class TestAlarmsEndpoint(CustomTestCase):
19
15
  self.response_items = {"id", "name", "description", "criticality", "schema"}
20
16
  self.items_to_check = ["name", "description", "schema", "criticality"]
21
17
 
22
- @unittest.skipUnless(
23
- int(os.getenv("CF_ALARMS_ENDPOINT")) == 1, "No alarms implemented"
24
- )
25
18
  def test_post_alarm(self):
26
- payload = {
27
- "name": "Alarm 1",
28
- "description": "Description Alarm 1",
29
- "criticality": 1,
30
- }
19
+ payload = {"name": "Alarm 1", "description": "Description Alarm 1", "criticality": 1}
31
20
  self.create_new_row(self.url, self.model, payload)
32
21
 
33
- @unittest.skipUnless(
34
- int(os.getenv("CF_ALARMS_ENDPOINT")) == 1, "No alarms implemented"
35
- )
36
22
  def test_get_alarms(self):
37
23
  data = [
38
24
  {"name": "Alarm 1", "description": "Description Alarm 1", "criticality": 1},
39
- {
40
- "name": "Alarm 2",
41
- "description": "Description Alarm 2",
42
- "criticality": 2,
43
- "schema": "solve_model_dag",
44
- },
25
+ {"name": "Alarm 2", "description": "Description Alarm 2", "criticality": 2, "schema": "solve_model_dag"},
45
26
  ]
46
- rows = self.get_rows(self.url, data, check_data=False)
27
+ rows = self.get_rows(
28
+ self.url,
29
+ data,
30
+ check_data=False
31
+ )
47
32
  rows_data = list(rows.json)
48
33
  for i in range(len(data)):
49
34
  for key in self.get_keys_to_check(data[i]):
50
35
  self.assertIn(key, rows_data[i])
51
36
  if key in data[i]:
52
37
  self.assertEqual(rows_data[i][key], data[i][key])
38
+
39
+
@@ -20,11 +20,6 @@ from cornflow.shared.exceptions import NoPermission, ObjectDoesNotExist
20
20
  class CLITests(TestCase):
21
21
  def setUp(self):
22
22
  db.create_all()
23
- self.number_of_views = 52
24
- self.number_of_permissions = 574
25
- if int(os.getenv("CF_ALARMS_ENDPOINT", 0)) != 1:
26
- self.number_of_views = 50
27
- self.number_of_permissions = 519
28
23
 
29
24
  def tearDown(self):
30
25
  db.session.remove()
@@ -136,7 +131,7 @@ class CLITests(TestCase):
136
131
  result = runner.invoke(cli, ["views", "init", "-v"])
137
132
  self.assertEqual(result.exit_code, 0)
138
133
  views = ViewModel.get_all_objects().all()
139
- self.assertEqual(len(views), self.number_of_views)
134
+ self.assertEqual(len(views), 49)
140
135
 
141
136
  def test_permissions_entrypoint(self):
142
137
  runner = CliRunner()
@@ -160,8 +155,8 @@ class CLITests(TestCase):
160
155
  permissions = PermissionViewRoleModel.get_all_objects().all()
161
156
  self.assertEqual(len(actions), 5)
162
157
  self.assertEqual(len(roles), 4)
163
- self.assertEqual(len(views), self.number_of_views)
164
- self.assertEqual(len(permissions), self.number_of_permissions)
158
+ self.assertEqual(len(views), 49)
159
+ self.assertEqual(len(permissions), 546)
165
160
 
166
161
  def test_permissions_base_command(self):
167
162
  runner = CliRunner()
@@ -176,8 +171,8 @@ class CLITests(TestCase):
176
171
  permissions = PermissionViewRoleModel.get_all_objects().all()
177
172
  self.assertEqual(len(actions), 5)
178
173
  self.assertEqual(len(roles), 4)
179
- self.assertEqual(len(views), self.number_of_views)
180
- self.assertEqual(len(permissions), self.number_of_permissions)
174
+ self.assertEqual(len(views), 49)
175
+ self.assertEqual(len(permissions), 546)
181
176
 
182
177
  def test_service_entrypoint(self):
183
178
  runner = CliRunner()
@@ -1,5 +1,5 @@
1
1
  import json
2
- import os
2
+
3
3
  from flask_testing import TestCase
4
4
 
5
5
  from cornflow.app import (
@@ -48,11 +48,7 @@ class TestCommands(TestCase):
48
48
  "email": "testemail@test.org",
49
49
  "password": "Testpassword1!",
50
50
  }
51
-
52
- if int(os.getenv("CF_ALARMS_ENDPOINT")) == 1:
53
- self.resources = resources + alarms_resources
54
- else:
55
- self.resources = resources
51
+ self.resources = resources + alarms_resources
56
52
  self.runner = self.create_app().test_cli_runner()
57
53
  self.runner.invoke(register_roles, ["-v"])
58
54
 
@@ -57,7 +57,6 @@ class TestExecutionsListEndpoint(BaseTestCases.ListFilters):
57
57
  "instance_id",
58
58
  "name",
59
59
  "indicators",
60
- "reports",
61
60
  ]
62
61
 
63
62
  def test_new_execution(self):
@@ -261,7 +260,6 @@ class TestExecutionsDetailEndpointMock(CustomTestCase):
261
260
  "schema",
262
261
  "user_id",
263
262
  "indicators",
264
- "reports",
265
263
  }
266
264
  # we only check the following because this endpoint does not return data
267
265
  self.items_to_check = ["name", "description"]
@@ -305,7 +303,6 @@ class TestExecutionsDetailEndpoint(
305
303
  "name",
306
304
  "created_at",
307
305
  "state",
308
- "reports",
309
306
  ]
310
307
  execution = self.get_one_row(
311
308
  self.url + idx,
@@ -411,7 +408,6 @@ class TestExecutionsDataEndpoint(TestExecutionsDetailEndpointMock):
411
408
  "state",
412
409
  "name",
413
410
  "id",
414
- "reports",
415
411
  ]
416
412
 
417
413
  def test_get_one_execution(self):
@@ -454,7 +450,6 @@ class TestExecutionsLogEndpoint(TestExecutionsDetailEndpointMock):
454
450
  "user_id",
455
451
  "config",
456
452
  "indicators",
457
- "reports",
458
453
  ]
459
454
 
460
455
  def test_get_one_execution(self):
@@ -2,8 +2,6 @@
2
2
 
3
3
  """
4
4
 
5
- import unittest
6
- import os
7
5
  import json
8
6
 
9
7
  # Imports from internal modules
@@ -31,9 +29,6 @@ class TestMainAlarmsEndpoint(CustomTestCase):
31
29
  headers=self.get_header_with_auth(self.token),
32
30
  ).json["id"]
33
31
 
34
- @unittest.skipUnless(
35
- int(os.getenv("CF_ALARMS_ENDPOINT")) == 1, "No alarms implemented"
36
- )
37
32
  def test_post_main_alarm(self):
38
33
  payload = {
39
34
  "message": "Message Main Alarm 1",
@@ -42,9 +37,6 @@ class TestMainAlarmsEndpoint(CustomTestCase):
42
37
  }
43
38
  self.create_new_row(self.url, self.model, payload)
44
39
 
45
- @unittest.skipUnless(
46
- int(os.getenv("CF_ALARMS_ENDPOINT")) == 1, "No alarms implemented"
47
- )
48
40
  def test_get_main_alarms(self):
49
41
  data = [
50
42
  {
@@ -363,11 +363,7 @@ class TestUserEndpoint(TestCase):
363
363
 
364
364
  def test_change_password_rotation(self):
365
365
  current_app.config["PWD_ROTATION_TIME"] = 1 # in days
366
- payload = {
367
- "pwd_last_change": (datetime.utcnow() - timedelta(days=2)).strftime(
368
- "%Y-%m-%dT%H:%M:%SZ"
369
- )
370
- }
366
+ payload = {"pwd_last_change": (datetime.utcnow() - timedelta(days=2)).strftime("%Y-%m-%dT%H:%M:%SZ")}
371
367
  self.modify_info(self.planner, self.planner, payload)
372
368
  response = self.log_in(self.planner)
373
369
  self.assertEqual(True, response.json["change_password"])
@@ -375,6 +371,7 @@ class TestUserEndpoint(TestCase):
375
371
  payload = {"password": "Newtestpassword1!"}
376
372
  self.modify_info(self.planner, self.planner, payload)
377
373
  self.planner.update(payload)
374
+ print(self.planner)
378
375
  response = self.log_in(self.planner)
379
376
  self.assertEqual(False, response.json["change_password"])
380
377
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cornflow
3
- Version: 1.1.1a1
3
+ Version: 1.1.2
4
4
  Summary: Cornflow is an open source multi-solver optimization server with a REST API built using flask.
5
5
  Home-page: https://github.com/baobabsoluciones/cornflow
6
6
  Author: baobab soluciones
@@ -15,14 +15,14 @@ Requires-Python: >=3.8
15
15
  Requires-Dist: alembic==1.9.2
16
16
  Requires-Dist: apispec<=6.2.0
17
17
  Requires-Dist: click<=8.1.3
18
- Requires-Dist: cornflow-client==1.1.1a1
18
+ Requires-Dist: cornflow-client>=1.1.0
19
19
  Requires-Dist: cryptography<=42.0.5
20
20
  Requires-Dist: disposable-email-domains>=0.0.86
21
21
  Requires-Dist: Flask==2.3.2
22
22
  Requires-Dist: flask-apispec<=0.11.4
23
23
  Requires-Dist: Flask-Bcrypt<=1.0.1
24
24
  Requires-Dist: Flask-Compress<=1.13
25
- Requires-Dist: flask-cors<=4.0.1
25
+ Requires-Dist: flask-cors>=4.0.2
26
26
  Requires-Dist: flask-inflate<=0.3
27
27
  Requires-Dist: Flask-Migrate<=4.0.4
28
28
  Requires-Dist: Flask-RESTful<=0.3.9
@@ -32,14 +32,14 @@ Requires-Dist: gunicorn<=22.0.0
32
32
  Requires-Dist: jsonpatch<=1.32
33
33
  Requires-Dist: ldap3<=2.9.1
34
34
  Requires-Dist: marshmallow<=3.19.0
35
- Requires-Dist: PuLP<=2.7.0
35
+ Requires-Dist: PuLP<=2.9.0
36
36
  Requires-Dist: psycopg2<=2.95
37
- Requires-Dist: PyJWT<=2.6.0,>=2.0.0
37
+ Requires-Dist: PyJWT<=2.6.0
38
38
  Requires-Dist: pytups>=0.86.2
39
- Requires-Dist: requests<=2.31.0
39
+ Requires-Dist: requests<=2.32.3
40
40
  Requires-Dist: SQLAlchemy==1.3.21
41
41
  Requires-Dist: webargs<=8.2.0
42
- Requires-Dist: Werkzeug<=3.0.3
42
+ Requires-Dist: Werkzeug==3.0.6
43
43
  Requires-Dist: greenlet<=2.0.2; python_version < "3.11"
44
44
  Requires-Dist: greenlet==3.0.0; python_version >= "3.11"
45
45