1919from lean .components .config .project_config_manager import ProjectConfigManager
2020from lean .components .util .logger import Logger
2121from lean .components .util .project_manager import ProjectManager
22- from lean .models .api import QCLanguage , QCProject , QCLeanEnvironment
22+ from lean .models .api import QCLanguage , QCProject
2323from lean .models .utils import LeanLibraryReference
2424
2525
@@ -43,6 +43,7 @@ def __init__(self,
4343 self ._project_manager = project_manager
4444 self ._project_config_manager = project_config_manager
4545 self ._last_file = None
46+ self ._cloud_projects = []
4647
4748 def push_projects (self , projects_to_push : List [Path ], organization_id : Optional [str ] = None ) -> None :
4849 """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[
5253 :param projects_to_push: a list of directories containing the local projects that need to be pushed
5354 :param organization_id: the id of the organization where the project will be pushed to
5455 """
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
5958
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 )
6263
6364 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 ())
6767 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 )
7170 except Exception as ex :
7271 self ._logger .debug (traceback .format_exc ().strip ())
7372 if self ._last_file is not None :
7473 self ._logger .warn (f"Cannot push '{ relative_path } ' (failed on { self ._last_file } ): { ex } " )
7574 else :
7675 self ._logger .warn (f"Cannot push '{ relative_path } ': { ex } " )
7776
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 )
8378
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 :
8680 for path , project in projects .items ():
8781 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 )
9184
9285 def _get_local_libraries_cloud_ids (self , project_dir : Path ) -> List [int ]:
9386 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]:
10093
10194 return local_libraries_cloud_ids
10295
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
10698
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 :
111100 libraries_to_add = [library_id for library_id in local_libraries_cloud_ids if
112101 library_id not in project .libraries ]
113102
114103 if len (libraries_to_add ) > 0 :
115104 self ._logger .info (f"Adding libraries to project { project .name } in the cloud" )
116105
117106 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 )
119108 self ._logger .info (f"[{ i } /{ len (libraries_to_add )} ] "
120109 f"Adding library { library_name } to project { project .name } in the cloud" )
121110 self ._api_client .projects .add_library (project .projectId , library_cloud_id )
122111
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 :
127113 libraries_to_remove = [library_id for library_id in project .libraries
128114 if library_id not in local_libraries_cloud_ids ]
129115
130116 if len (libraries_to_remove ) > 0 :
131117 self ._logger .info (f"Removing libraries from project { project .name } in the cloud" )
132118
133119 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 )
135121 self ._logger .info (f"[{ i } /{ len (libraries_to_remove )} ] "
136122 f"Removing library { library_name } from project { project .name } in the cloud" )
137123 self ._api_client .projects .delete_library (project .projectId , library_cloud_id )
138124
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 :
144126 """Pushes a single local project to the cloud.
145127
146128 Raises an error with a descriptive message if the project cannot be pushed.
147129
148130 :param project: the local project to push
149- :param cloud_projects: a list containing all of the user's cloud projects
150131 :param organization_id: the id of the organization to push the project to
151- :param environments: list of available lean environments
152132 """
153133 project_name = project .relative_to (Path .cwd ()).as_posix ()
154134
155135 project_config = self ._project_config_manager .get_project_config (project )
156136 cloud_id = project_config .get ("cloud-id" )
157137
158- cloud_project_by_id = next (iter ([p for p in cloud_projects if p .projectId == cloud_id ]), None )
159-
160138 # 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 :
162140 # 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 )
164142 else :
165143 # 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 )
169150
170151 organization_message_part = f" in organization '{ organization_id } '" if organization_id is not None else ""
171152 self ._logger .info (f"Successfully created cloud project '{ project_name } '{ organization_message_part } " )
172153
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-
181154 # Push local files to cloud
182155 self ._push_files (project , cloud_project )
183156
184157 # Finalize pushing by updating locally modified metadata
185- self ._push_metadata (project , cloud_project , environments )
158+ self ._push_metadata (project , cloud_project )
186159
187160 return cloud_project
188161
@@ -225,7 +198,7 @@ def _push_files(self, project: Path, cloud_project: QCProject) -> None:
225198
226199 self ._last_file = None
227200
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 :
229202 """Pushes local project description and parameters to the cloud.
230203
231204 Does nothing if the cloud is already up-to-date.
@@ -245,8 +218,7 @@ def _push_metadata(self, project: Path, cloud_project: QCProject, environments:
245218 local_lean_version = int (project_config .get ("lean-engine" , "-1" ))
246219 cloud_lean_version = cloud_project .leanVersionId
247220
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 )
250222 cloud_lean_venv = cloud_project .leanEnvironment
251223
252224 update_args = {}
@@ -258,12 +230,23 @@ def _push_metadata(self, project: Path, cloud_project: QCProject, environments:
258230 update_args ["parameters" ] = local_parameters
259231
260232 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 )):
262234 update_args ["lean_engine" ] = local_lean_version
263235
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 :
265240 update_args ["python_venv" ] = local_lean_venv
266241
267242 if update_args != {}:
268243 self ._api_client .projects .update (cloud_project .projectId , ** update_args )
269244 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