From 4cdb033a05eb8fcd18c4b3c605beacbb8fe4a30d Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Wed, 16 Feb 2022 03:05:27 -0300 Subject: [PATCH 01/10] Update indent --- GithubCloner2022.py | 4 ++-- Github_Repositories.csv | 4 ++-- Modules/DataFrameHandler_Mod/__init__.py | 8 ++++---- Modules/GetData_Mod/DataManager.py | 10 +++++----- Modules/GetData_Mod/__init__.py | 8 ++++---- Modules/PrintMessage_Mod/__init__.py | 8 ++++---- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/GithubCloner2022.py b/GithubCloner2022.py index 0e519bc..8d04698 100644 --- a/GithubCloner2022.py +++ b/GithubCloner2022.py @@ -45,10 +45,10 @@ # ?#########? End DataManager Configuration ########## # ?#########? Start DataFrame Configuration ########## - #* Reads the 'csv' File to get the dataframe + # *# Reads the 'csv' File to get the dataframe df = pd.read_csv(filename) - #* Sets the Main DF to the class to handle it + # *# Sets the Main DF to the class to handle it Handler.MainDataFrame = df Handler.ConfigsJsonValues = JsonDFConfigs Handler.ConfigurateDataFrame(Handler.ConfigsJsonValues['Course']) diff --git a/Github_Repositories.csv b/Github_Repositories.csv index ce50003..129509f 100644 --- a/Github_Repositories.csv +++ b/Github_Repositories.csv @@ -1,6 +1,6 @@ "Marca temporal","Nombre/s","Apellido/s","División","DNI / Legajo","E-Mail","Link al repositorio" -"2022/02/13 10:26:52 p. m. GMT-3","Neptune","Romane God","1G - Professor 1 - Helper 1","222222","neptune@notplanet.com","https://github.com/caidevOficial/SPD2022_TPS.git" -"2022/02/13 10:26:52 p. m. GMT-3","Poseidon","Grecian God","1F - Professor 2 - Helper 2","333333","poseidon@sea.com","https://github.com/caidevOficial/Python_ITBA_IEEE.git" +"2022/02/13 10:26:52 p. m. GMT-3","Neptune","Romane God","1G - Professor 1 - Helper 1","222222","neptune@notplanet.com","https://github.com/caidevOficial/SPD2022_TPS" +"2022/02/13 10:26:52 p. m. GMT-3","Poseidon","Grecian God","1F - Professor 2 - Helper 2","333333","poseidon@sea.com","https://github.com/caidevOficial/Python_ITBA_IEEE" "2022/02/13 10:26:52 p. m. GMT-3","Hades","Grecian God","1F - Professor 2 - Helper 2","111111","Hades@underworld.com","https://github.com/caidevOficial/CaidevOficial.git" "2022/02/13 10:26:52 p. m. GMT-3","Zeus","Grecian God","1G - Professor 1 - Helper 1","444444","zeus@ray.com","https://github.com/caidevOficial/Python_IEEE_Team14293.git" "2022/02/13 10:26:52 p. m. GMT-3","Mercury","Romane God","1G - Professor 1 - Helper 1","222222","neptune@notplanet.com","https://github.com/caidevOficial/SPD2022_TPS.git" diff --git a/Modules/DataFrameHandler_Mod/__init__.py b/Modules/DataFrameHandler_Mod/__init__.py index 89927cb..7d112a3 100644 --- a/Modules/DataFrameHandler_Mod/__init__.py +++ b/Modules/DataFrameHandler_Mod/__init__.py @@ -1,16 +1,16 @@ # GNU General Public License V3 -# +# # Copyright (c) 2022 [FacuFalcone] -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . diff --git a/Modules/GetData_Mod/DataManager.py b/Modules/GetData_Mod/DataManager.py index fb27275..c51ae37 100644 --- a/Modules/GetData_Mod/DataManager.py +++ b/Modules/GetData_Mod/DataManager.py @@ -274,9 +274,9 @@ def NormalizeURL(self, url: str) -> str: str: [The normalized url]. \n """ if not ".git" in url: - url = url.replace(' \n', '') - url = f'{url}.git' - return url.replace("\ \n", "") + url = url.replace('\n', '.git') + #url = f'{url}.git' + return url.replace("\n", "") def NormalizeCourse(self, course: str) -> str: """[summary] \n @@ -364,10 +364,10 @@ def CloneRepositories(self, DfH: DFH, ) -> None: self.Messenger.PrintMessage() try: - #? Create git Clone commands + # ?## Create git Clone commands self.MakeCloneCommands(DfH) - #? Execute the commands + # ?## Execute the commands self.ExecuteCommands(self.Messenger) self.Messenger.SetMessage('All Repositories have been cloned!') diff --git a/Modules/GetData_Mod/__init__.py b/Modules/GetData_Mod/__init__.py index 89927cb..7d112a3 100644 --- a/Modules/GetData_Mod/__init__.py +++ b/Modules/GetData_Mod/__init__.py @@ -1,16 +1,16 @@ # GNU General Public License V3 -# +# # Copyright (c) 2022 [FacuFalcone] -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . diff --git a/Modules/PrintMessage_Mod/__init__.py b/Modules/PrintMessage_Mod/__init__.py index 89927cb..7d112a3 100644 --- a/Modules/PrintMessage_Mod/__init__.py +++ b/Modules/PrintMessage_Mod/__init__.py @@ -1,16 +1,16 @@ # GNU General Public License V3 -# +# # Copyright (c) 2022 [FacuFalcone] -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . From b7e6952b6a29954bfffd22a2c88450373ca3fc11 Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Wed, 16 Feb 2022 03:12:36 -0300 Subject: [PATCH 02/10] Update Format Code --- Modules/DataFrameHandler_Mod/DFHandler.py | 6 +++--- Modules/GetData_Mod/DataManager.py | 11 +++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Modules/DataFrameHandler_Mod/DFHandler.py b/Modules/DataFrameHandler_Mod/DFHandler.py index 47c6154..0404f12 100644 --- a/Modules/DataFrameHandler_Mod/DFHandler.py +++ b/Modules/DataFrameHandler_Mod/DFHandler.py @@ -232,10 +232,10 @@ def ConfigurateDataFrame(self, columnValue: str) -> None: columnValue (str): [The column value to configurate]. \n """ - #* Gets the unique values of the column 'columnValue' [Division] + # *# Gets the unique values of the column 'columnValue' [Division] self.ConfigUniqueValuesInColumn(columnValue) - #* For each unique value of the column 'columnValue' [Division] - #* Creates a list of dataframes with the students that have the specified value in the column 'columnValue' [Division] + # *# For each unique value of the column 'columnValue' [Division] + # *# Creates a list of dataframes with the students that have the specified value in the column 'columnValue' [Division] for unique in self.UniqueColumns: self.CreateListDFStudentsBy( self.MainDataFrame, columnValue, unique) diff --git a/Modules/GetData_Mod/DataManager.py b/Modules/GetData_Mod/DataManager.py index c51ae37..3c59b23 100644 --- a/Modules/GetData_Mod/DataManager.py +++ b/Modules/GetData_Mod/DataManager.py @@ -273,9 +273,9 @@ def NormalizeURL(self, url: str) -> str: Returns: str: [The normalized url]. \n """ - if not ".git" in url: + if ".git" not in url: url = url.replace('\n', '.git') - #url = f'{url}.git' + # url = f'{url}.git' return url.replace("\n", "") def NormalizeCourse(self, course: str) -> str: @@ -319,7 +319,7 @@ def MakeCloneCommands(self, dfHandler: DFH) -> None: Args: surname (str): [The surname of the student]. \n course (str): [The course of the student]. \n - git (str): [The url of the git's repository]. \n + git (str): [The url of the git's repository]. \n """ for frame in dfHandler.OrderListOfDFStudents: self.MakeCloneCommandsForDF(frame, dfHandler) @@ -336,7 +336,10 @@ def MakeCloneCommandsForDF(self, df: DataFrame, dfHandler: DFH) -> None: surnameStr = df[dfHandler.ConfigsJsonValues['Surname']][i] nameStr = df[dfHandler.ConfigsJsonValues['Name']][i] message = f"Cloning {surnameStr}, {nameStr}'s repository from {crudeCourse}" - command = f"git clone {self.NormalizeURL(df[dfHandler.ConfigsJsonValues['GitLink']][i])} {courseStr}//{self.FormatFullnameDate(surnameStr, nameStr)}" + normalizedURL = self.NormalizeURL( + df[dfHandler.ConfigsJsonValues['GitLink']][i]) + normalizedFullname = self.FormatFullnameDate(surnameStr, nameStr) + command = f"git clone {normalizedURL} {courseStr}//{normalizedFullname}" self.AddComand(command) self.CloningMessages = message From 6339d2d450630587e372d7e854975cc5a0dc4a52 Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Wed, 16 Feb 2022 03:15:25 -0300 Subject: [PATCH 03/10] Update Format Code --- Modules/DataFrameHandler_Mod/DFHandler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/DataFrameHandler_Mod/DFHandler.py b/Modules/DataFrameHandler_Mod/DFHandler.py index 0404f12..905ed64 100644 --- a/Modules/DataFrameHandler_Mod/DFHandler.py +++ b/Modules/DataFrameHandler_Mod/DFHandler.py @@ -235,7 +235,8 @@ def ConfigurateDataFrame(self, columnValue: str) -> None: # *# Gets the unique values of the column 'columnValue' [Division] self.ConfigUniqueValuesInColumn(columnValue) # *# For each unique value of the column 'columnValue' [Division] - # *# Creates a list of dataframes with the students that have the specified value in the column 'columnValue' [Division] + # *# Creates a list of dataframes with the students that have the + # *# specified value in the column 'columnValue' [Division] for unique in self.UniqueColumns: self.CreateListDFStudentsBy( self.MainDataFrame, columnValue, unique) From 0a394392c57039cf444d0e3f3dff4afdb74c1765 Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Wed, 16 Feb 2022 03:16:47 -0300 Subject: [PATCH 04/10] Update Format Code --- Modules/DataFrameHandler_Mod/DFHandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/DataFrameHandler_Mod/DFHandler.py b/Modules/DataFrameHandler_Mod/DFHandler.py index 905ed64..5fd36c6 100644 --- a/Modules/DataFrameHandler_Mod/DFHandler.py +++ b/Modules/DataFrameHandler_Mod/DFHandler.py @@ -235,7 +235,7 @@ def ConfigurateDataFrame(self, columnValue: str) -> None: # *# Gets the unique values of the column 'columnValue' [Division] self.ConfigUniqueValuesInColumn(columnValue) # *# For each unique value of the column 'columnValue' [Division] - # *# Creates a list of dataframes with the students that have the + # *# Creates a list of dataframes with the students that have the # *# specified value in the column 'columnValue' [Division] for unique in self.UniqueColumns: self.CreateListDFStudentsBy( From 8b0cbccbcf634e250c059740b37120f7065ddba6 Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Wed, 16 Feb 2022 22:47:25 -0300 Subject: [PATCH 05/10] deleted deprecated files --- Modules/GetData_Mod/DataManager.py | 383 ----------------------------- Modules/GetData_Mod/__init__.py | 16 -- 2 files changed, 399 deletions(-) delete mode 100644 Modules/GetData_Mod/DataManager.py delete mode 100644 Modules/GetData_Mod/__init__.py diff --git a/Modules/GetData_Mod/DataManager.py b/Modules/GetData_Mod/DataManager.py deleted file mode 100644 index 3c59b23..0000000 --- a/Modules/GetData_Mod/DataManager.py +++ /dev/null @@ -1,383 +0,0 @@ -# GNU General Public License V3 -# -# Copyright (c) 2022 [FacuFalcone] -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import os - -import requests -from Modules.DataFrameHandler_Mod.DFHandler import DataFrameHandler as DFH -from Modules.PrintMessage_Mod.CloneMessenger import CloneMessenger as CM -from pandas import DataFrame - - -class DataManager: - """[summary] \n - Class in charge of the file management to read and process the \n - data of the students in order to clone their repositorys. \n - Returns: - [class]: [DataManager]. \n - """ - # ?########? START ATTRIBUTES ######### - __configAPIURL = '' - __name = '' - __version = '' - __author = '' - __commands: list = [] - __Messenger: CM = CM() - __studentsInfo: DataFrame = DataFrame() - __APIResponse = None - __cloningMessages: list = [] - # ?#######? END ATTRIBUTES ######### - - def __init__(self): - pass - - def InitialConfig(self, name: str, version: str, author: str, APIURL: dict): - """[summary] \n - Initialize the config of the class. \n - Args: - name (str): [The name of the program]. \n - version (str): [The version of the program]. \n - author (str): [The author of the program]. \n - APIURL (dict): [The API URL of the program]. \n - """ - self.SetAppName(name) - self.SetAppVersion(version) - self.AppAuthor = author - self.SetAPIURL(APIURL) - self.APIResponse = self.GetAPIURL() - - # ?########? SETTERS ######### - - def SetFilename(self, fileName: str) -> None: - """[summary] \n - Set the name of the file to read. \n - Args: - fileName (str): [The name of the file to read]. \n - """ - self.fileName = fileName - - def SetAppName(self, name: str) -> None: - """[summary] \n - Set the name of the file. \n - Args: - name (str): [The name of the file]. \n - """ - self.__name = name - - def SetAppVersion(self, version: str) -> None: - """[summary] \n - Set the version of the file. \n - Args: - version (str): [The version of the file]. \n - """ - self.__version = version - - def SetAPIURL(self, api: dict) -> None: - """[summary] \n - Set the URL of the API. \n - Args: \n - api (str): [The URL of the API] \n - "URL": "https://api.github.com/repos", \n - "USER": "CaidevOficial", \n - "REPO": "Python_Udemy_DataManipulation", \n - "BRANCH": "main" \n - Example: "https://api.github.com/repos/CaidevOficial/Python_Udemy_DataManipulation/commits/main". \n - """ - self.__configAPIURL = f'{api["URL"]}/{api["USER"]}/{api["REPO"]}/commits/{api["BRANCH"]}' - - def AddComand(self, command: str) -> None: - """[summary] \n - Add a command to the list of the commands. \n - Args: - command (str): [The command to add]. \n - """ - self.__commands.append(command) - - def SetStudentsDF(self, students: DataFrame) -> None: - """[summary] \n - Sets the dataframe of students to work with. \n - Args: - students (DataFrame): The dataframe of students to work. \n - """ - self.__studentsInfo = students - - # ?#######? GETTERS ######### - - def GetCommands(self) -> list: - """[summary] \n - Get the commands to clone the repositories of the students. \n - Returns: - list: [The list of the commands to execute]. \n - """ - return self.__commands - - def GetAppName(self) -> str: - """[summary] \n - Get the name of the application. \n - Returns: - str: [The name of the application]. \n - """ - return self.__name - - def GetAppVersion(self) -> str: - """[summary] \n - Get the version of the application. \n - Returns: - str: [The version of the application]. \n - """ - return self.__version - - def GetFilename(self) -> str: - """[summary] \n - Get the name of the file. \n - Returns: - str: [The name of the file]. \n - """ - return self.fileName - - def GetAPIURL(self) -> str: - """[summary] \n - Get the URL of the API. \n - Returns: - str: [The URL of the API]. \n - """ - return self.__configAPIURL - - def GetDate(self) -> str: - """[summary] \n - Get the date from the API. \n - Returns: - str: [The date formatted without the dashes]. \n - """ - date = self.APIResponse.json()["commit"]["author"]["date"] - date = date[:10] - return date.replace("-", "") - - def GetStudentsDF(self) -> DataFrame: - """[summary] \n - Gets the Students DataFrame of the class to work with. \n - Returns: - DataFrame: The Actual DataFrame of the students. \n - """ - return self.__studentsInfo - - # ?########? END GETTERS ######### - - # ?########? PROPERTIES ######### - - @property - def AppAuthor(self) -> str: - """[summary] \n - Get the author of the application. \n - Returns: - str: [The author of the application]. \n - """ - return self.__author - - @AppAuthor.setter - def AppAuthor(self, author: str) -> None: - """[summary] \n - Set the author of the application. \n - Args: - author (str): [The author of the application]. \n - """ - self.__author = author - - @property - def Messenger(self) -> CM: - """[summary] \n - Get the Messenger of the class. \n - Returns: - CM: [The Messenger of the class]. \n - """ - return self.__Messenger - - @property - def Commands(self): - """[summary] \n - Get the commands to clone the repositories of the students. \n - Returns: - list: [The list of the commands to execute]. \n - """ - return self.__commands - - @Commands.setter - def Commands(self, commands: list): - """[summary] \n - Set the commands to clone the repositories of the students. \n - Args: - commands (list): [The list of the commands to execute]. \n - """ - self.__commands = commands - - @property - def APIResponse(self) -> str: - """[summary] \n - Get the API Response. \n - Returns: - str: [The API Response]. \n - """ - return self.__APIResponse - - @APIResponse.setter - def APIResponse(self, APILink: str): - """[summary] \n - Set the API Response by sending a request trough the API link. \n - Args: - APILink (str): [The Link of the API]. \n - """ - self.__APIResponse = requests.get(APILink) - - @property - def CloningMessages(self) -> list: - """[summary] \n - Get the list of the cloning messages. \n - Returns: - list: [The list of the cloning messages]. \n - """ - return self.__cloningMessages - - @CloningMessages.setter - def CloningMessages(self, cloningMessage: str): - """[summary] \n - Adds a message to the cloning messages. \n - Args: - cloningMessage (str): [The message to add]. \n - """ - self.__cloningMessages.append(cloningMessage) - - # ?#######? END PROPERTIES ######### - - # ?########? METHODS ######### - - def NormalizeURL(self, url: str) -> str: - """[summary] \n - Normalize the URLs of the git's repositorys of the students, \n - adding the .git at the end if it is not already there. \n - Args: - url (str): [The crudal url of the git's repository]. \n - Returns: - str: [The normalized url]. \n - """ - if ".git" not in url: - url = url.replace('\n', '.git') - # url = f'{url}.git' - return url.replace("\n", "") - - def NormalizeCourse(self, course: str) -> str: - """[summary] \n - Normalize the course name, removing the spaces. \n - Args: - course (str): [The course name]. \n - Returns: - str: [The normalized course name]. \n - """ - return course.replace(' - ', '-').replace(" ", "_") - - def FormatFullnameDate(self, surname: str, name: str) -> str: - """[summary] \n - Format the surname of the student, removing the spaces and replacing them with '_' \n - Args: - surname (str): [The surname of the student]. \n - name (str): [The name of the student]. \n - date (str): [The date of the student]. \n - Returns: - str: [The formatted Fullname like this: surname_name_date]. \n - """ - surname = surname.replace(",", "_").replace(" ", "").replace(" \n", "") - name = name.replace(",", "_").replace(" ", "").replace(" \n", "") - - return f'{surname}_{name}_{self.GetDate()}' - - def FormatCourse(self, fieldList: str) -> str: - """[summary] \n - Format the course of the student, removing the line jumps and replacing them with '_' \n - Args: - fieldList (str): [The field of the course. It is the second field of the csv file]. \n - Returns: - str: [The formatted course]. \n - """ - return self.NormalizeCourse(fieldList.replace(" \n", "")) - - def MakeCloneCommands(self, dfHandler: DFH) -> None: - """[summary] \n - Make the commands to clone the repositories of the students. \n - Args: - surname (str): [The surname of the student]. \n - course (str): [The course of the student]. \n - git (str): [The url of the git's repository]. \n - """ - for frame in dfHandler.OrderListOfDFStudents: - self.MakeCloneCommandsForDF(frame, dfHandler) - - def MakeCloneCommandsForDF(self, df: DataFrame, dfHandler: DFH) -> None: - """[summary] \n - Make the commands to clone the repositories of the students. \n - Args: - df (DataFrame): [The DataFrame with the students information]. \n - """ - for i in df.index: - crudeCourse = df[dfHandler.ConfigsJsonValues['Course']][i] - courseStr = self.NormalizeCourse(self.FormatCourse(crudeCourse)) - surnameStr = df[dfHandler.ConfigsJsonValues['Surname']][i] - nameStr = df[dfHandler.ConfigsJsonValues['Name']][i] - message = f"Cloning {surnameStr}, {nameStr}'s repository from {crudeCourse}" - normalizedURL = self.NormalizeURL( - df[dfHandler.ConfigsJsonValues['GitLink']][i]) - normalizedFullname = self.FormatFullnameDate(surnameStr, nameStr) - command = f"git clone {normalizedURL} {courseStr}//{normalizedFullname}" - self.AddComand(command) - self.CloningMessages = message - - def ExecuteCommands(self, cloneMessenger: CM) -> None: - """[summary] \n - Execute the commands to clone the repositories of the students. \n - Args: - commandList (list): [The list of the commands to execute]. \n - """ - - commandList = [x.strip() for x in self.Commands] - messages = [x.strip() for x in self.CloningMessages] - - for command in commandList: - cloneMessenger.SetMessage(messages[commandList.index(command)]) - cloneMessenger.PrintMessage() - os.system(command) - - def CloneRepositories(self, DfH: DFH, ) -> None: - """[summary] \n - Open the file and get the data. \n - """ - appInfo = f'{self.GetAppName()} - {self.GetAppVersion()} by {self.AppAuthor}' - self.Messenger.SetMessage(appInfo) - self.Messenger.PrintMessage() - - try: - # ?## Create git Clone commands - self.MakeCloneCommands(DfH) - - # ?## Execute the commands - self.ExecuteCommands(self.Messenger) - - self.Messenger.SetMessage('All Repositories have been cloned!') - self.Messenger.PrintMessage() - - except Exception as e: - self.Messenger.SetMessage(f'Exception: {e.args}') - self.Messenger.PrintMessage() - - # ?########? END METHODS ######### diff --git a/Modules/GetData_Mod/__init__.py b/Modules/GetData_Mod/__init__.py deleted file mode 100644 index 7d112a3..0000000 --- a/Modules/GetData_Mod/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# GNU General Public License V3 -# -# Copyright (c) 2022 [FacuFalcone] -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . From 79079f72f91549637a0a8b1574c829f0ad653809 Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Wed, 16 Feb 2022 22:47:57 -0300 Subject: [PATCH 06/10] replace set&get for properties --- Modules/DataManager_Mod/DataManager.py | 377 +++++++++++++++++++++ Modules/Formatter_Mod/Formatter.py | 85 +++++ Modules/PrintMessage_Mod/CloneMessenger.py | 30 +- 3 files changed, 480 insertions(+), 12 deletions(-) create mode 100644 Modules/DataManager_Mod/DataManager.py create mode 100644 Modules/Formatter_Mod/Formatter.py diff --git a/Modules/DataManager_Mod/DataManager.py b/Modules/DataManager_Mod/DataManager.py new file mode 100644 index 0000000..29eb082 --- /dev/null +++ b/Modules/DataManager_Mod/DataManager.py @@ -0,0 +1,377 @@ +# GNU General Public License V3 +# +# Copyright (c) 2022 [FacuFalcone] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os +import requests + +from Modules.DataFrameHandler_Mod.DFHandler import DataFrameHandler as DFH +from Modules.PrintMessage_Mod.CloneMessenger import CloneMessenger as CM +from pandas import DataFrame + + +class DataManager: + """[summary] \n + Class in charge of the file management to read and process the \n + data of the students in order to clone their repositorys. \n + Returns: + [class]: [DataManager]. \n + """ + # ?########? START ATTRIBUTES ######### + __configAPIURL = '' + __APIResponse = None + __APIDate = None + __name = '' + __version = '' + __author = '' + __studentsInfo: DataFrame = DataFrame() + __commands: list = [] + __Messenger: CM = CM() + __cloningMessages: list = [] + # ?#######? END ATTRIBUTES ######### + + def __init__(self): + pass + + # ?########? PROPERTIES - GET ######### + + @property + def AppAuthor(self) -> str: + """[summary] \n + Get the author of the application. \n + Returns: + str: [The author of the application]. \n + """ + return self.__author + + @property + def Messenger(self) -> CM: + """[summary] \n + Get the Messenger of the class. \n + Returns: + CM: [The Messenger of the class]. \n + """ + return self.__Messenger + + @property + def Commands(self): + """[summary] \n + Get the commands to clone the repositories of the students. \n + Returns: + list: [The list of the commands to execute]. \n + """ + return self.__commands + + @property + def APIResponse(self) -> str: + """[summary] \n + Get the API Response. \n + Returns: + str: [The API Response]. \n + """ + return self.__APIResponse + + @property + def AppName(self) -> str: + """[summary] \n + Set the name of the file. \n + Args: + name (str): [The name of the file]. \n + """ + return self.__name + + @property + def AppVersion(self) -> str: + """[summary] \n + Get the version of the application. \n + Returns: + str: [The version of the application]. \n + """ + return self.__version + + @property + def APIURL(self) -> str: + """[summary] \n + Get the URL of the API. \n + Returns: + str: [The URL of the API]. \n + """ + return self.__configAPIURL + + @property + def StudensDF(self) -> DataFrame: + """[summary] \n + Get the dataframe with the students information. \n + Returns: + DataFrame: [The dataframe with the students information]. \n + """ + return self.__studentsInfo + + @property + def CloningMessages(self) -> list: + """[summary] \n + Get the list of the cloning messages. \n + Returns: + list: [The list of the cloning messages]. \n + """ + return self.__cloningMessages + + @property + def APIDate(self) -> str: + """[summary] \n + Get the date of the API. \n + Returns: + str: [The date of the API]. \n + """ + return self.__APIDate + + + # ?########? END PROPERTIES - GET ######### + + # ?########? START PROPERTIES - SET ######### + + @AppAuthor.setter + def AppAuthor(self, author: str) -> None: + """[summary] \n + Set the author of the application. \n + Args: + author (str): [The author of the application]. \n + """ + self.__author = author + + @Commands.setter + def Commands(self, commands: list): + """[summary] \n + Set the commands to clone the repositories of the students. \n + Args: + commands (list): [The list of the commands to execute]. \n + """ + self.__commands = commands + + @APIResponse.setter + def APIResponse(self, APILink: str): + """[summary] \n + Set the API Response by sending a request trough the API link. \n + Args: + APILink (str): [The Link of the API]. \n + """ + self.__APIResponse = requests.get(APILink) + + @AppName.setter + def AppName(self, name: str) -> None: + """[summary] \n + Set the name of the file. \n + Args: + name (str): [The name of the file]. \n + """ + self.__name = name + + @AppVersion.setter + def AppVersion(self, version: str) -> None: + """[summary] \n + Set the version of the file. \n + Args: + version (str): [The version of the file]. \n + """ + self.__version = version + + @APIURL.setter + def APIURL(self, url: dict) -> None: + """[summary] \n + Set the URL of the API. \n + Args: \n + url (dict): [The json with all the fields of the API url.] \n + "URL": "https://api.github.com/repos", \n + "USER": "Your_Github_User", \n + "REPO": "Your_Repository", \n + "BRANCH": "Your_Principal_Branch" \n + Example: "https://api.github.com/repos/Your_Github_User/Your_Repository/commits/Your_Principal_Branch". \n + """ + self.__configAPIURL = f'{url["URL"]}/{url["USER"]}/{url["REPO"]}/commits/{url["BRANCH"]}' + + @StudensDF.setter + def StudensDF(self, studentsInfo: DataFrame) -> None: + """[summary] \n + Set the dataframe of students to work with. \n + Args: + studentsInfo (DataFrame): The dataframe of students to work. \n + """ + self.__studentsInfo = studentsInfo + + @CloningMessages.setter + def CloningMessages(self, cloningMessage: str): + """[summary] \n + Adds a message to the cloning messages. \n + Args: + cloningMessage (str): [The message to add]. \n + """ + self.__cloningMessages.append(cloningMessage) + + @APIDate.setter + def APIDate(self, APIResponse: str): + """[summary] \n + Set the date of the API. \n + Args: + APIResponse (str): [The response of the API Setted]. \n + """ + date = APIResponse.json()["commit"]["author"]["date"] + date = date[:10] + self.__APIDate = date.replace("-", "") + + # ?#######? END PROPERTIES - SET ######### + + # ?########? METHODS ######### + + def InitialConfig(self, name: str, version: str, author: str, APIURL: dict): + """[summary] \n + Initialize the config of the class, Also sets the API response \n + and the date of the API. \n + Args: + name (str): [The name of the program]. \n + version (str): [The version of the program]. \n + author (str): [The author of the program]. \n + APIURL (dict): [The API URL of the program]. \n + """ + self.AppName = name + self.AppVersion = version + self.AppAuthor = author + self.APIURL = APIURL + self.APIResponse = self.APIURL + self.APIDate = self.APIResponse + + def AddComand(self, command: str) -> None: + """[summary] \n + Add a command to the list of the commands. \n + Args: + command (str): [The command to add]. \n + """ + self.__commands.append(command) + + def NormalizeURL(self, url: str) -> str: + """[summary] \n + Normalize the URLs of the git's repositorys of the students, \n + adding the .git at the end if it is not already there. \n + Args: + url (str): [The crudal url of the git's repository]. \n + Returns: + str: [The normalized url]. \n + """ + if ".git" not in url: + url = url.replace('\n', '.git') + return url.replace("\n", "") + + def NormalizeCourse(self, course: str) -> str: + """[summary] \n + Normalize the course name, removing the spaces. \n + Args: + course (str): [The course name]. \n + Returns: + str: [The normalized course name]. \n + """ + return course.replace(' - ', '-').replace(" ", "_") + + def FormatFullnameDate(self, surname: str, name: str) -> str: + """[summary] \n + Format the surname of the student, removing the spaces and replacing them with '_' \n + Args: + surname (str): [The surname of the student]. \n + name (str): [The name of the student]. \n + date (str): [The date of the student]. \n + Returns: + str: [The formatted Fullname like this: surname_name_date]. \n + """ + surname = surname.replace(",", "_").replace(" ", "").replace(" \n", "") + name = name.replace(",", "_").replace(" ", "").replace(" \n", "") + + return f'{surname}_{name}_{self.APIDate}' + + def FormatCourse(self, fieldList: str) -> str: + """[summary] \n + Format the course of the student, removing the line jumps and replacing them with '_' \n + Args: + fieldList (str): [The field of the course. It is the second field of the csv file]. \n + Returns: + str: [The formatted course]. \n + """ + return self.NormalizeCourse(fieldList.replace(" \n", "")) + + def MakeCloneCommands(self, dfHandler: DFH) -> None: + """[summary] \n + Make the commands to clone the repositories of the students. \n + Args: + surname (str): [The surname of the student]. \n + course (str): [The course of the student]. \n + git (str): [The url of the git's repository]. \n + """ + for frame in dfHandler.OrderListOfDFStudents: + self.MakeCloneCommandsForDF(frame, dfHandler) + + def MakeCloneCommandsForDF(self, df: DataFrame, dfHandler: DFH) -> None: + """[summary] \n + Make the commands to clone the repositories of the students. \n + Args: + df (DataFrame): [The DataFrame with the students information]. \n + """ + for i in df.index: + crudeCourse = df[dfHandler.ConfigsJsonValues['Course']][i] + courseStr = self.NormalizeCourse(self.FormatCourse(crudeCourse)) + surnameStr = df[dfHandler.ConfigsJsonValues['Surname']][i] + nameStr = df[dfHandler.ConfigsJsonValues['Name']][i] + message = f"Cloning the repository of: {surnameStr}, {nameStr} from {crudeCourse}" + normalizedURL = self.NormalizeURL( + df[dfHandler.ConfigsJsonValues['GitLink']][i]) + normalizedFullname = self.FormatFullnameDate(surnameStr, nameStr) + command = f"git clone {normalizedURL} {courseStr}//{normalizedFullname}" + self.AddComand(command) + self.CloningMessages = message + + def ExecuteCommands(self, cloneMessenger: CM) -> None: + """[summary] \n + Execute the commands to clone the repositories of the students. \n + Args: + commandList (list): [The list of the commands to execute]. \n + """ + commandList = [x.strip() for x in self.Commands] + messages = [x.strip() for x in self.CloningMessages] + + for command in commandList: + cloneMessenger.Message = messages[commandList.index(command)] + cloneMessenger.PrintMessage() + os.system(command) + + def CloneRepositories(self, DfH: DFH, ) -> None: + """[summary] \n + Open the file and get the data. \n + """ + appInfo = f'{self.AppName} - {self.AppVersion} by {self.AppAuthor}' + self.Messenger.Message = appInfo + self.Messenger.PrintMessage() + + try: + # ?## Create git Clone commands + self.MakeCloneCommands(DfH) + + # ?## Execute the commands + self.ExecuteCommands(self.Messenger) + + self.Messenger.Message = 'All Repositories have been cloned!' + self.Messenger.PrintMessage() + + except Exception as e: + self.Messenger.Message = f'Exception: {e.args}' + self.Messenger.PrintMessage() + + # ?########? END METHODS ######### diff --git a/Modules/Formatter_Mod/Formatter.py b/Modules/Formatter_Mod/Formatter.py new file mode 100644 index 0000000..b0a5574 --- /dev/null +++ b/Modules/Formatter_Mod/Formatter.py @@ -0,0 +1,85 @@ +# GNU General Public License V3 +# +# Copyright (c) 2022 [FacuFalcone] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import datetime + + +class Formatter: + """[summary] \n + Formats the time saved in the attribute of the class, as a string. \n + like: {:02.0f} minute(s) {:02.0f} seconds. \n + Returns: + class: [Formatter]. \n + """ + + # ?###########? START ATTRIBUTES ############ + __crudeTime = None + __formattedTimeStr: str = '' + # ?###########? END ATTRIBUTES ############ + + def __init__(self) -> None: + pass + + # ?######? START PROPERTIES ####### + + @property + def CrudeTime(self) -> datetime: + return self.__crudeTime + + @property + def FormattedTimeStr(self) -> str: + """[summary] \n + Gets the formatted time string. + Returns: + str: [The formatted time string.] + """ + return self.__formattedTimeStr + + @CrudeTime.setter + def CrudeTime(self, value: datetime) -> None: + """[summary] \n + Sets the time into the class and format it as a string like: \n + {:02.0f} minute(s) {:02.0f} seconds. \n + Args: + value (datetime): [Time to be formatted and saved into the class.] + """ + self.__crudeTime = value + self.__FormatDatetimeAsString() + + @FormattedTimeStr.setter + def FormattedTimeStr(self, formatTime: str) -> None: + """[summary] \n + Sets the formatted time as a string. + Args: + start_time (datetime): [Formatted Time] \n + """ + self.__formattedTimeStr = formatTime + + # ?###########? END PROPERTIES ############ + + # ?###########? START METHODS ############ + + def __FormatDatetimeAsString(self) -> None: + """[summary] \n + Formats the time saved in the attribute of the class, as a string. \n + like: {:02.0f} minute(s) {:02.0f} seconds. \n + """ + seconds = (datetime.datetime.now() - self.CrudeTime).total_seconds() + m, s = divmod(seconds, 60) + self.FormattedTimeStr = "{:02.0f} minute(s) {:02.0f} seconds".format(m, s) + + # ?###########? END METHODS ############ diff --git a/Modules/PrintMessage_Mod/CloneMessenger.py b/Modules/PrintMessage_Mod/CloneMessenger.py index e3f12dd..7721a0a 100644 --- a/Modules/PrintMessage_Mod/CloneMessenger.py +++ b/Modules/PrintMessage_Mod/CloneMessenger.py @@ -28,22 +28,28 @@ class CloneMessenger: def __init__(self) -> None: pass - def SetMessage(self, message: str) -> None: - """[summary] \n - Sets the message of the class. \n - Args: - message (str): The message to be printed in the console. \n - """ - self.__message = message + # ?######? START PROPERTIES ####### - def GetMessage(self) -> str: + @property + def Message(self) -> str: """[summary] \n - Gets the message of the class. \n + Get the message of the class. \n Returns: - str: Message of the class to be printed in the console. \n + str: Message of the class. \n """ return self.__message + @Message.setter + def Message(self, message: str) -> None: + """[summary] \n + Sets the message of the class. \n + Args: + message (str): The message to be printed in the console. \n + """ + self.__message = message + + # ?######? END PROPERTIES ####### + # ?###########? START METHODS ############ def InitializeMessenger(self, message: str) -> None: @@ -63,7 +69,7 @@ def PrintMessage(self) -> None: print( ' \n', f'{symbols} \n', - f'{self.GetMessage()} \n', + f'{self.Message} \n', f'{symbols} \n' ) @@ -73,6 +79,6 @@ def GenerateSymbols(self) -> str: Returns: str: String of symbols of the same length of the message of the class. \n """ - return ''.join(['#' for i in range(len(self.GetMessage()))]) + return ''.join(['#' for i in range(len(self.Message))]) # ?###########? END METHODS ############ From e13227e038272d549a82058294c9339b598d4f7c Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Wed, 16 Feb 2022 22:48:14 -0300 Subject: [PATCH 07/10] Update documentation --- Github_Repositories.csv | 9 +++++++-- Media/FinalMessage.png | Bin 0 -> 17715 bytes README.md | 25 ++++++++++++++++++++----- 3 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 Media/FinalMessage.png diff --git a/Github_Repositories.csv b/Github_Repositories.csv index 129509f..dee755d 100644 --- a/Github_Repositories.csv +++ b/Github_Repositories.csv @@ -1,7 +1,12 @@ "Marca temporal","Nombre/s","Apellido/s","División","DNI / Legajo","E-Mail","Link al repositorio" -"2022/02/13 10:26:52 p. m. GMT-3","Neptune","Romane God","1G - Professor 1 - Helper 1","222222","neptune@notplanet.com","https://github.com/caidevOficial/SPD2022_TPS" +"2022/02/13 10:26:52 p. m. GMT-3","Neptune","Romane God","1G - Professor 1 - Helper 1","222222","neptune@notplanet.com","https://github.com/caidevOficial/Python_RepositoryCloner" "2022/02/13 10:26:52 p. m. GMT-3","Poseidon","Grecian God","1F - Professor 2 - Helper 2","333333","poseidon@sea.com","https://github.com/caidevOficial/Python_ITBA_IEEE" "2022/02/13 10:26:52 p. m. GMT-3","Hades","Grecian God","1F - Professor 2 - Helper 2","111111","Hades@underworld.com","https://github.com/caidevOficial/CaidevOficial.git" -"2022/02/13 10:26:52 p. m. GMT-3","Zeus","Grecian God","1G - Professor 1 - Helper 1","444444","zeus@ray.com","https://github.com/caidevOficial/Python_IEEE_Team14293.git" +"2022/02/13 10:26:52 p. m. GMT-3","Zeus","Grecian God","1G - Professor 1 - Helper 1","444444","zeus@ray.com","https://github.com/caidevOficial/Python_RepositoryCloner.git" "2022/02/13 10:26:52 p. m. GMT-3","Mercury","Romane God","1G - Professor 1 - Helper 1","222222","neptune@notplanet.com","https://github.com/caidevOficial/SPD2022_TPS.git" "2022/02/13 10:26:52 p. m. GMT-3","Artemisa","Grecian God","1G - Professor 1 - Helper 1","444444","zeus@ray.com","https://github.com/caidevOficial/Python_IEEE_Team14293.git" +"2022/02/13 10:26:52 p. m. GMT-3","Helios","Romane God","1F - Professor 2 - Helper 2","555555","Helios@notsun.com","https://github.com/caidevOficial/Python_RepositoryCloner" +"2022/02/13 10:26:52 p. m. GMT-3","Odin","Nordic God","1G - Professor 1 - Helper 1","777777","odin@fatherofall.com","https://github.com/caidevOficial/Python_RepositoryCloner.git" +"2022/02/13 10:26:52 p. m. GMT-3","Thor","Nordic God","1F - Professor 2 - Helper 2","888888","thor@thundergod.com","https://github.com/caidevOficial/Python_RepositoryCloner" +"2022/02/13 10:26:52 p. m. GMT-3","Loki","Nordic God","1G - Professor 1 - Helper 1","888888","loki@trapgod.com","https://github.com/caidevOficial/Python_RepositoryCloner" +"2022/02/13 10:26:52 p. m. GMT-3","Valhalla","Nordic Reign","1F - Professor 2 - Helper 2","999999","valhalla@nordicreign.com","https://github.com/caidevOficial/Python_RepositoryCloner" diff --git a/Media/FinalMessage.png b/Media/FinalMessage.png new file mode 100644 index 0000000000000000000000000000000000000000..5f602d68f6cd15cf22225d13fbaa22b51af26520 GIT binary patch literal 17715 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 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 2f39c67..0c919a4 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,21 @@ Meanwhile the program is cloning the repositories, the console will show message +When finish, you look a final message (with the elapsed time of the execution) like this: + + + + + + + + + + +
Console Final Message
+ Console Messages Image +
+


@@ -200,9 +215,9 @@ In order to use this Cloner, you should configure the file [API_Info.json](./Mod [ "Github": { "URL": "https://api.github.com/repos", - "USER": "CaidevOficial", - "REPO": "Python_Udemy_DataManipulation", - "BRANCH": "main" + "USER": "YOUR_GITHUB_USER", + "REPO": "YOUR_REPOSITORY_NAME", + "BRANCH": "THE_PRINCIPAL_BRANCH_NAME" }, "DataFrame": { "Fields": { @@ -224,7 +239,7 @@ for example: { "URL": "https://api.github.com/repos", "USER": "CaidevOficial", - "REPO": "Python_Udemy_DataManipulation", + "REPO": "Python_RepositoryCloner", "BRANCH": "main" }, "DataFrame": { @@ -243,7 +258,7 @@ for example: Then the code will make the link like: ``` -https://api.github.com/repos/CaidevOficial/Python_Udemy_DataManipulation/commits/main +https://api.github.com/repos/CaidevOficial/Python_RepositoryCloner/commits/main ``` This way the program will take the 'Date' of the last commit of the branch 'main' and will use it to create the folder with the name of the repository. Obviously, the repository MUST BE PUBLIC, otherwise the program won't be able to access its API. From 26b26e72344cdbf3dc983b82d4e25dc7342f4810 Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Wed, 16 Feb 2022 22:48:35 -0300 Subject: [PATCH 08/10] Create new modules init --- Modules/DataManager_Mod/__init__.py | 16 ++++++++++++++++ Modules/Formatter_Mod/__init__.py | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 Modules/DataManager_Mod/__init__.py create mode 100644 Modules/Formatter_Mod/__init__.py diff --git a/Modules/DataManager_Mod/__init__.py b/Modules/DataManager_Mod/__init__.py new file mode 100644 index 0000000..7d112a3 --- /dev/null +++ b/Modules/DataManager_Mod/__init__.py @@ -0,0 +1,16 @@ +# GNU General Public License V3 +# +# Copyright (c) 2022 [FacuFalcone] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . diff --git a/Modules/Formatter_Mod/__init__.py b/Modules/Formatter_Mod/__init__.py new file mode 100644 index 0000000..7d112a3 --- /dev/null +++ b/Modules/Formatter_Mod/__init__.py @@ -0,0 +1,16 @@ +# GNU General Public License V3 +# +# Copyright (c) 2022 [FacuFalcone] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . From 608e1be133bf5869c99b4a34731d9298ea922552 Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Wed, 16 Feb 2022 22:48:59 -0300 Subject: [PATCH 09/10] Update main files to V2.1.12 --- GithubCloner2022.py | 95 +++++++++++++++++++++++++++---------------- Modules/API_Info.json | 2 +- 2 files changed, 62 insertions(+), 35 deletions(-) diff --git a/GithubCloner2022.py b/GithubCloner2022.py index 8d04698..e785fec 100644 --- a/GithubCloner2022.py +++ b/GithubCloner2022.py @@ -15,48 +15,75 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import datetime import pandas as pd from Modules.DataFrameHandler_Mod.DFHandler import DataFrameHandler as DfH -from Modules.GetData_Mod.DataManager import DataManager as DM +from Modules.DataManager_Mod.DataManager import DataManager as DM +from Modules.Formatter_Mod.Formatter import Formatter as FMT +from Modules.PrintMessage_Mod.CloneMessenger import CloneMessenger as CM # ?######### Start Basic Configuration ########## filename = 'Github_Repositories.csv' name = 'Github Repository Cloner' -version = '[V2.0.11]' +version = '[V2.0.12]' author = '[FacuFalcone - CaidevOficial]' fileConfigName = 'Modules/API_Info.json' # ?######### End Basic Configuration ########## -try: - # ?#########? Start Initialization ########## - JsonFile = pd.read_json(f"./{fileConfigName}", orient='records') - JsonAPI = JsonFile['Github'] - JsonDFConfigs = JsonFile['DataFrame']['Fields'] - # ?#########? End Initialization ############ - - # ?#########? Start Objects Instances ########## - Handler = DfH() - Manager = DM() - # ?#########? End Objects Instances ########## - - # ?#########? Start DataManager Configuration ########## - Manager.InitialConfig(name, version, author, JsonAPI) - # ?#########? End DataManager Configuration ########## - - # ?#########? Start DataFrame Configuration ########## - # *# Reads the 'csv' File to get the dataframe - df = pd.read_csv(filename) - - # *# Sets the Main DF to the class to handle it - Handler.MainDataFrame = df - Handler.ConfigsJsonValues = JsonDFConfigs - Handler.ConfigurateDataFrame(Handler.ConfigsJsonValues['Course']) - # ?#########? End DataFrame Configuration ########## - - # ?#########? Start Initialize DataManager ########## - Manager.CloneRepositories(Handler) - # ?##########? End Initialize DataManager ########### - -except Exception as e: - print(f'Exception: {e.args}') +if __name__ == '__main__': + + start_time = datetime.datetime.now() + + try: + # ?#########? Start Initialization ########## + JsonFile = pd.read_json(f"./{fileConfigName}", orient='records') + JsonAPI = JsonFile['Github'] + JsonDFConfigs = JsonFile['DataFrame']['Fields'] + # ?#########? End Initialization ############ + + # ?#########? Start Objects Instances ########## + Handler = DfH() + Manager = DM() + Messenger = CM() + Timer = FMT() + # ?#########? End Objects Instances ########## + + # ?#########? Start DataManager Configuration ########## + Manager.InitialConfig(name, version, author, JsonAPI) + # ?#########? End DataManager Configuration ########## + + # ?#########? Start DataFrame Configuration ########## + # *# Reads the 'csv' File to get the dataframe + df = pd.read_csv(filename) + + # *# Sets the Main DF to the class to handle it + Handler.MainDataFrame = df + Handler.ConfigsJsonValues = JsonDFConfigs + Handler.ConfigurateDataFrame(Handler.ConfigsJsonValues['Course']) + # ?#########? End DataFrame Configuration ########## + + # ?#########? Start Initialize DataManager ########## + Manager.CloneRepositories(Handler) + # ?##########? End Initialize DataManager ########### + + + except Exception as e: + print(f'Exception: {e.args}') + finally: + # ?#########? Start Timer Config ########## + Timer.CrudeTime = start_time + # ?#########? End Timer Config ########## + + # ?#########? Start Print Message ########## + Messenger.Message = f"Elapsed Time: {Timer.FormattedTimeStr}" + Messenger.PrintMessage() + + Messenger.Message = f"Thanks for using {name} {version} by {author}! ♥" + Messenger.PrintMessage() + + Messenger.Message = "Success! All task done. Press a key to close the app" + Messenger.PrintMessage() + # ?#########? End Print Message ########## + + end = input() diff --git a/Modules/API_Info.json b/Modules/API_Info.json index 8b92de3..a14c202 100644 --- a/Modules/API_Info.json +++ b/Modules/API_Info.json @@ -2,7 +2,7 @@ "Github": { "URL": "https://api.github.com/repos", "USER": "CaidevOficial", - "REPO": "Python_Udemy_DataManipulation", + "REPO": "Python_RepositoryCloner", "BRANCH": "main" }, "DataFrame": { From 4122887271cefcc76268ec0d63a1b6fefe99c4ff Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Wed, 16 Feb 2022 22:58:22 -0300 Subject: [PATCH 10/10] deleted blank spaces --- GithubCloner2022.py | 3 +-- Modules/DataManager_Mod/DataManager.py | 11 ++++------- Modules/PrintMessage_Mod/CloneMessenger.py | 4 ++-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/GithubCloner2022.py b/GithubCloner2022.py index e785fec..7e38e44 100644 --- a/GithubCloner2022.py +++ b/GithubCloner2022.py @@ -34,7 +34,6 @@ if __name__ == '__main__': start_time = datetime.datetime.now() - try: # ?#########? Start Initialization ########## JsonFile = pd.read_json(f"./{fileConfigName}", orient='records') @@ -67,7 +66,7 @@ Manager.CloneRepositories(Handler) # ?##########? End Initialize DataManager ########### - + except Exception as e: print(f'Exception: {e.args}') finally: diff --git a/Modules/DataManager_Mod/DataManager.py b/Modules/DataManager_Mod/DataManager.py index 29eb082..cf69766 100644 --- a/Modules/DataManager_Mod/DataManager.py +++ b/Modules/DataManager_Mod/DataManager.py @@ -101,7 +101,7 @@ def AppVersion(self) -> str: str: [The version of the application]. \n """ return self.__version - + @property def APIURL(self) -> str: """[summary] \n @@ -110,7 +110,7 @@ def APIURL(self) -> str: str: [The URL of the API]. \n """ return self.__configAPIURL - + @property def StudensDF(self) -> DataFrame: """[summary] \n @@ -119,7 +119,7 @@ def StudensDF(self) -> DataFrame: DataFrame: [The dataframe with the students information]. \n """ return self.__studentsInfo - + @property def CloningMessages(self) -> list: """[summary] \n @@ -137,7 +137,6 @@ def APIDate(self) -> str: str: [The date of the API]. \n """ return self.__APIDate - # ?########? END PROPERTIES - GET ######### @@ -201,7 +200,7 @@ def APIURL(self, url: dict) -> None: Example: "https://api.github.com/repos/Your_Github_User/Your_Repository/commits/Your_Principal_Branch". \n """ self.__configAPIURL = f'{url["URL"]}/{url["USER"]}/{url["REPO"]}/commits/{url["BRANCH"]}' - + @StudensDF.setter def StudensDF(self, studentsInfo: DataFrame) -> None: """[summary] \n @@ -295,7 +294,6 @@ def FormatFullnameDate(self, surname: str, name: str) -> str: """ surname = surname.replace(",", "_").replace(" ", "").replace(" \n", "") name = name.replace(",", "_").replace(" ", "").replace(" \n", "") - return f'{surname}_{name}_{self.APIDate}' def FormatCourse(self, fieldList: str) -> str: @@ -359,7 +357,6 @@ def CloneRepositories(self, DfH: DFH, ) -> None: appInfo = f'{self.AppName} - {self.AppVersion} by {self.AppAuthor}' self.Messenger.Message = appInfo self.Messenger.PrintMessage() - try: # ?## Create git Clone commands self.MakeCloneCommands(DfH) diff --git a/Modules/PrintMessage_Mod/CloneMessenger.py b/Modules/PrintMessage_Mod/CloneMessenger.py index 7721a0a..35f658b 100644 --- a/Modules/PrintMessage_Mod/CloneMessenger.py +++ b/Modules/PrintMessage_Mod/CloneMessenger.py @@ -47,9 +47,9 @@ def Message(self, message: str) -> None: message (str): The message to be printed in the console. \n """ self.__message = message - + # ?######? END PROPERTIES ####### - + # ?###########? START METHODS ############ def InitializeMessenger(self, message: str) -> None: