19
19
from lean .components .config .project_config_manager import ProjectConfigManager
20
20
from lean .components .util .logger import Logger
21
21
from lean .components .util .project_manager import ProjectManager
22
- from lean .models .api import QCLanguage , QCProject , QCLeanEnvironment
22
+ from lean .models .api import QCLanguage , QCProject
23
23
from lean .models .utils import LeanLibraryReference
24
24
25
25
@@ -43,6 +43,7 @@ def __init__(self,
43
43
self ._project_manager = project_manager
44
44
self ._project_config_manager = project_config_manager
45
45
self ._last_file = None
46
+ self ._cloud_projects = []
46
47
47
48
def push_projects (self , projects_to_push : List [Path ], organization_id : Optional [str ] = None ) -> None :
48
49
"""Pushes the given projects from the local drive to the cloud.
@@ -52,42 +53,34 @@ def push_projects(self, projects_to_push: List[Path], organization_id: Optional[
52
53
:param projects_to_push: a list of directories containing the local projects that need to be pushed
53
54
:param organization_id: the id of the organization where the project will be pushed to
54
55
"""
55
- projects = projects_to_push + [library
56
- for project in projects_to_push
57
- for library in self ._project_manager .get_project_libraries (project )]
58
- projects = sorted (projects )
56
+ if len (projects_to_push ) == 0 :
57
+ return
59
58
60
- cloud_projects = self ._api_client .projects .get_all ()
61
- environments = self ._api_client .lean .environments ()
59
+ projects_paths = projects_to_push + [library
60
+ for project in projects_to_push
61
+ for library in self ._project_manager .get_project_libraries (project )]
62
+ projects_paths = sorted (projects_paths )
62
63
63
64
pushed_projects = {}
64
-
65
- for index , project in enumerate (projects , start = 1 ):
66
- relative_path = project .relative_to (Path .cwd ())
65
+ for index , path in enumerate (projects_paths , start = 1 ):
66
+ relative_path = path .relative_to (Path .cwd ())
67
67
try :
68
- self ._logger .info (f"[{ index } /{ len (projects )} ] Pushing '{ relative_path } '" )
69
- pushed_project = self ._push_project (project , cloud_projects , organization_id , environments )
70
- pushed_projects [project ] = pushed_project
68
+ self ._logger .info (f"[{ index } /{ len (projects_paths )} ] Pushing '{ relative_path } '" )
69
+ pushed_projects [path ] = self ._push_project (path , organization_id )
71
70
except Exception as ex :
72
71
self ._logger .debug (traceback .format_exc ().strip ())
73
72
if self ._last_file is not None :
74
73
self ._logger .warn (f"Cannot push '{ relative_path } ' (failed on { self ._last_file } ): { ex } " )
75
74
else :
76
75
self ._logger .warn (f"Cannot push '{ relative_path } ': { ex } " )
77
76
78
- pushed_cloud_projects = pushed_projects .values ()
79
- cloud_projects = [project for project in cloud_projects if project .projectId not in pushed_cloud_projects ]
80
- cloud_projects .extend (pushed_cloud_projects )
81
-
82
- self ._update_cloud_library_references (pushed_projects , cloud_projects )
77
+ self ._update_cloud_library_references (pushed_projects )
83
78
84
- def _update_cloud_library_references (self , projects : Dict [Path , QCProject ],
85
- cloud_projects : List [QCProject ]) -> None :
79
+ def _update_cloud_library_references (self , projects : Dict [Path , QCProject ]) -> None :
86
80
for path , project in projects .items ():
87
81
local_libraries_cloud_ids = self ._get_local_libraries_cloud_ids (path )
88
-
89
- self ._add_new_libraries (project , local_libraries_cloud_ids , cloud_projects )
90
- self ._remove_outdated_libraries (project , local_libraries_cloud_ids , cloud_projects )
82
+ self ._add_new_libraries (project , local_libraries_cloud_ids )
83
+ self ._remove_outdated_libraries (project , local_libraries_cloud_ids )
91
84
92
85
def _get_local_libraries_cloud_ids (self , project_dir : Path ) -> List [int ]:
93
86
project_config = self ._project_config_manager .get_project_config (project_dir )
@@ -100,89 +93,69 @@ def _get_local_libraries_cloud_ids(self, project_dir: Path) -> List[int]:
100
93
101
94
return local_libraries_cloud_ids
102
95
103
- @staticmethod
104
- def _get_library_name (library_cloud_id : int , cloud_projects : List [QCProject ]) -> str :
105
- return [project .name for project in cloud_projects if project .projectId == library_cloud_id ][0 ]
96
+ def _get_library_name (self , library_cloud_id : int ) -> str :
97
+ return self ._get_cloud_project (library_cloud_id ).name
106
98
107
- def _add_new_libraries (self ,
108
- project : QCProject ,
109
- local_libraries_cloud_ids : List [int ],
110
- cloud_projects : List [QCProject ]) -> None :
99
+ def _add_new_libraries (self , project : QCProject , local_libraries_cloud_ids : List [int ]) -> None :
111
100
libraries_to_add = [library_id for library_id in local_libraries_cloud_ids if
112
101
library_id not in project .libraries ]
113
102
114
103
if len (libraries_to_add ) > 0 :
115
104
self ._logger .info (f"Adding libraries to project { project .name } in the cloud" )
116
105
117
106
for i , library_cloud_id in enumerate (libraries_to_add , start = 1 ):
118
- library_name = self ._get_library_name (library_cloud_id , cloud_projects )
107
+ library_name = self ._get_library_name (library_cloud_id )
119
108
self ._logger .info (f"[{ i } /{ len (libraries_to_add )} ] "
120
109
f"Adding library { library_name } to project { project .name } in the cloud" )
121
110
self ._api_client .projects .add_library (project .projectId , library_cloud_id )
122
111
123
- def _remove_outdated_libraries (self ,
124
- project : QCProject ,
125
- local_libraries_cloud_ids : List [int ],
126
- cloud_projects : List [QCProject ]) -> None :
112
+ def _remove_outdated_libraries (self , project : QCProject , local_libraries_cloud_ids : List [int ]) -> None :
127
113
libraries_to_remove = [library_id for library_id in project .libraries
128
114
if library_id not in local_libraries_cloud_ids ]
129
115
130
116
if len (libraries_to_remove ) > 0 :
131
117
self ._logger .info (f"Removing libraries from project { project .name } in the cloud" )
132
118
133
119
for i , library_cloud_id in enumerate (libraries_to_remove , start = 1 ):
134
- library_name = self ._get_library_name (library_cloud_id , cloud_projects )
120
+ library_name = self ._get_library_name (library_cloud_id )
135
121
self ._logger .info (f"[{ i } /{ len (libraries_to_remove )} ] "
136
122
f"Removing library { library_name } from project { project .name } in the cloud" )
137
123
self ._api_client .projects .delete_library (project .projectId , library_cloud_id )
138
124
139
- def _push_project (self ,
140
- project : Path ,
141
- cloud_projects : List [QCProject ],
142
- organization_id : Optional [str ],
143
- environments : List [QCLeanEnvironment ]) -> QCProject :
125
+ def _push_project (self , project : Path , organization_id : Optional [str ]) -> QCProject :
144
126
"""Pushes a single local project to the cloud.
145
127
146
128
Raises an error with a descriptive message if the project cannot be pushed.
147
129
148
130
:param project: the local project to push
149
- :param cloud_projects: a list containing all of the user's cloud projects
150
131
:param organization_id: the id of the organization to push the project to
151
- :param environments: list of available lean environments
152
132
"""
153
133
project_name = project .relative_to (Path .cwd ()).as_posix ()
154
134
155
135
project_config = self ._project_config_manager .get_project_config (project )
156
136
cloud_id = project_config .get ("cloud-id" )
157
137
158
- cloud_project_by_id = next (iter ([p for p in cloud_projects if p .projectId == cloud_id ]), None )
159
-
160
138
# Find the cloud project to push the files to
161
- if cloud_project_by_id is not None :
139
+ if cloud_id is not None :
162
140
# Project has cloud id which matches cloud project, update cloud project
163
- cloud_project = cloud_project_by_id
141
+ cloud_project = self . _get_cloud_project ( cloud_id )
164
142
else :
165
143
# Project has invalid cloud id or no cloud id at all, create new cloud project
166
- new_project = self ._api_client .projects .create (project_name ,
167
- QCLanguage [project_config .get ("algorithm-language" )],
168
- organization_id )
144
+ cloud_project = self ._api_client .projects .create (project_name ,
145
+ QCLanguage [project_config .get ("algorithm-language" )],
146
+ organization_id )
147
+ self ._cloud_projects .append (cloud_project )
148
+ project_config .set ("cloud-id" , cloud_project .projectId )
149
+ project_config .set ("organization-id" , cloud_project .organizationId )
169
150
170
151
organization_message_part = f" in organization '{ organization_id } '" if organization_id is not None else ""
171
152
self ._logger .info (f"Successfully created cloud project '{ project_name } '{ organization_message_part } " )
172
153
173
- project_config .set ("cloud-id" , new_project .projectId )
174
-
175
- # We need to retrieve the created project again to get all project details
176
- cloud_project = self ._api_client .projects .get (new_project .projectId )
177
-
178
- # set organization-id in project config
179
- project_config .set ("organization-id" , cloud_project .organizationId )
180
-
181
154
# Push local files to cloud
182
155
self ._push_files (project , cloud_project )
183
156
184
157
# Finalize pushing by updating locally modified metadata
185
- self ._push_metadata (project , cloud_project , environments )
158
+ self ._push_metadata (project , cloud_project )
186
159
187
160
return cloud_project
188
161
@@ -225,7 +198,7 @@ def _push_files(self, project: Path, cloud_project: QCProject) -> None:
225
198
226
199
self ._last_file = None
227
200
228
- def _push_metadata (self , project : Path , cloud_project : QCProject , environments : List [ QCLeanEnvironment ] ) -> None :
201
+ def _push_metadata (self , project : Path , cloud_project : QCProject ) -> None :
229
202
"""Pushes local project description and parameters to the cloud.
230
203
231
204
Does nothing if the cloud is already up-to-date.
@@ -245,8 +218,7 @@ def _push_metadata(self, project: Path, cloud_project: QCProject, environments:
245
218
local_lean_version = int (project_config .get ("lean-engine" , "-1" ))
246
219
cloud_lean_version = cloud_project .leanVersionId
247
220
248
- default_lean_venv = next ((env .id for env in environments if env .path is None ), None )
249
- local_lean_venv = project_config .get ("python-venv" , default_lean_venv )
221
+ local_lean_venv = project_config .get ("python-venv" , None )
250
222
cloud_lean_venv = cloud_project .leanEnvironment
251
223
252
224
update_args = {}
@@ -258,12 +230,23 @@ def _push_metadata(self, project: Path, cloud_project: QCProject, environments:
258
230
update_args ["parameters" ] = local_parameters
259
231
260
232
if (local_lean_version != cloud_lean_version and
261
- (local_lean_version != - 1 or not cloud_project .leanPinnedToMaster )):
233
+ (local_lean_version != - 1 or not cloud_project .leanPinnedToMaster )):
262
234
update_args ["lean_engine" ] = local_lean_version
263
235
264
- if local_lean_venv != cloud_lean_venv :
236
+ # Initially, python-venv is not defined in the config and the default one will be used.
237
+ # After it is changed, in order to use the default one again, it must not be removed from the config,
238
+ # but it should be set to the default env id explicitly instead.
239
+ if local_lean_venv is not None and local_lean_venv != cloud_lean_venv :
265
240
update_args ["python_venv" ] = local_lean_venv
266
241
267
242
if update_args != {}:
268
243
self ._api_client .projects .update (cloud_project .projectId , ** update_args )
269
244
self ._logger .info (f"Successfully updated { ' and ' .join (update_args .keys ())} for '{ cloud_project .name } '" )
245
+
246
+ def _get_cloud_project (self , project_id : int ) -> QCProject :
247
+ project = next (iter (p for p in self ._cloud_projects if p .projectId == project_id ), None )
248
+ if project is None :
249
+ project = self ._api_client .projects .get (project_id )
250
+ self ._cloud_projects .append (project )
251
+
252
+ return project
0 commit comments