diff --git a/DataFrameHandler_Mod/DFHandler.py b/DataFrameHandler_Mod/DFHandler.py deleted file mode 100644 index 3e1cd89..0000000 --- a/DataFrameHandler_Mod/DFHandler.py +++ /dev/null @@ -1,238 +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 numpy import ndarray -from pandas import DataFrame - -class DataFrameHandler: - """[summary]\n - Class in charge of configurate and handle all the dataframe operations.\n - Returns: - class: [DataFrameHandler].\n - """ - #####? Attributes ##### - __configsJsonValues:dict = {} - __commands = [] - __uniqueColumns:ndarray = [] - __mainDF:DataFrame = None - __studentsDF = [] - __orderedListOfDFStudents:list = [] - #####? End Attributes ##### - - def __init__(self) -> None: - pass - - #####? Properties ##### - - @property - def OrderListOfDFStudents(self)->list: - """[summary]\n - Get the list of ordered dataframes.\n - Returns: - [list]: [The list of ordered dataframes].\n - """ - return self.__orderedListOfDFStudents - - @OrderListOfDFStudents.setter - def OrderListOfDFStudents(self, df:DataFrame)->None: - """[summary]\n - Set the list of ordered dataframes.\n - Args: - df (DataFrame): [The ordered dataframes].\n - """ - self.__orderedListOfDFStudents.append(df) - - @property - def ConfigsJsonValues(self)->dict: - """[summary]\n - Get the configs of the json.\n - Returns: - [dict]: [The configs of the json].\n - """ - return self.__configsJsonValues - - @ConfigsJsonValues.setter - def ConfigsJsonValues(self, value:dict)->None: - """[summary]\n - Set the configs of the json.\n - Args: - value (dict): [The configs of the json].\n - """ - self.__configsJsonValues = value - - @property - def MainDataFrame(self)->DataFrame: - """[summary]\n - Get the main dataframe.\n - Returns: - [DataFrame]: [The main dataframe].\n - """ - return self.__mainDF - - @MainDataFrame.setter - def MainDataFrame(self, df:DataFrame): - """[summary]\n - Set the main dataframe.\n - Args: - df (DataFrame): [The dataframe to set].\n - """ - self.__mainDF = df - - @property - def UniqueColumns(self) -> ndarray: - """[summary]\n - Get the unique values of the column 'columnValue' [Division].\n - Returns: - [ndarray]: [The unique values of the column 'columnValue' [Division]].\n - """ - return self.__uniqueColumns - - @UniqueColumns.setter - def UniqueColumns(self, uniqueColumns:ndarray): - """[summary]\n - Set the unique values of the column 'columnValue' [Division].\n - Args: - uniqueColumns (ndarray): [The unique values of the column 'columnValue' [Division]].\n - """ - self.__uniqueColumns = uniqueColumns - - @property - def Commands(self)->list: - """[summary]\n - Get the list of the commands.\n - Returns: - [list]: [The list of the commands].\n - """ - return self.__commands - - @Commands.setter - def Commands(self, command:str)->None: - """[summary]\n - Sets a command inside the list of the commands.\n - Args: - command (str): [The command to add].\n - """ - self.__commands.append(command) - - @property - def StudentsDF(self)->list: - """[summary]\n - Get the dataframe with the students.\n - Returns: - [DataFrame]: [The dataframe with the students].\n - """ - return self.__studentsDF - - @StudentsDF.setter - def StudentsDF(self, df:DataFrame)->None: - """[summary]\n - Set the dataframe with the students.\n - Args: - df (DataFrame): [The dataframe with the students].\n - """ - self.__studentsDF.append(df) - - #####? End Properties ##### - - #####? Methods ##### - - def GetSpecificStudentsDF(self, df:DataFrame, column:str, value:str)->DataFrame: - """[summary]\n - Get the students that have the specified index value in the specified column.\n - The DataFrame MUST be indexed by the 'value' column.\n - Args: - df (DataFrame): [The dataframe to filter].\n - column (str): [The column to filter].\n - value (str): [The value to filter].\n - Returns: - [DataFrame]: [The dataframe with the filtered students ordered by Course, Surname & Name].\n - """ - specificDF: DataFrame = df[df[column] == value] - orderedDF: DataFrame = self.OrderIndexedDFBy( - specificDF, self.ConfigsJsonValues['Course'], - self.ConfigsJsonValues['Surname'], - self.ConfigsJsonValues['Name'] - ) - return orderedDF - - def CreateListDFStudentsBy(self, df:DataFrame, column:str, columnValue:str): - """[summary]\n - Creates a list of the students that have the specified index value in the specified column.\n - The DataFrame MUST be indexed by the 'value' column.\n - Args: - df (DataFrame): [The dataframe to filter].\n - column (str): [The column to filter].\n - value (list): [The values to filter].\n - """ - self.OrderListOfDFStudents = self.GetSpecificStudentsDF(df, column, columnValue) - - def OrderIndexedDFBy(self, df:DataFrame, firstField:str, secondField:str, thirdField:str)->DataFrame: - """[summary]\n - Order the dataframe by the specified fields.\n - Args: - df (DataFrame): [The dataframe to order].\n - firstField (str): [The first field to order].\n - secondField (str): [The second field to order].\n - thirdField (str): [The third field to order].\n - Returns: - [DataFrame]: [The dataframe ordered by the three fields in the specified order].\n - """ - sortedDF = df.sort_values(by=[firstField, secondField, thirdField], ascending=[True, True, True]) - return sortedDF - - def ConfigUniqueValuesInColumn(self, column:str): - """[summary]\n - Get the unique values in the specified column and sort them in alphabetical order ASC.\n - Args: - column (str): [The column to filter].\n - Returns: - [list]: [The unique values in the specified column].\n - """ - self.UniqueColumns = self.MainDataFrame[column].unique() - self.UniqueColumns.sort() - - def CreateJsonOfEveryDF(self): - """[summary]\n - Create a json file for every dataframe.\n - """ - - for i in range(len(self.OrderListOfDFStudents)): - frame:DataFrame = self.OrderListOfDFStudents[i] - name = frame.at[frame.index.values[0], self.ConfigsJsonValues['Course']] - filename:str = f'{name}.json' - frame.to_json(f'{filename}', orient='table', indent=4, force_ascii=True) - - #####? End Methods ##### - - #####* Main Method ##### - - def ConfigurateDataFrame(self, columnValue:str)->None: - """[summary]\n - Configurate the dataframe with the specified column value.\n - Args: - columnValue (str): [The column value to configurate].\n - """ - - #* 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 unique in self.UniqueColumns: - self.CreateListDFStudentsBy(self.MainDataFrame, columnValue, unique) - self.CreateJsonOfEveryDF() - - #####* End Main Method ##### diff --git a/GetData_Mod/DataManager.py b/GetData_Mod/DataManager.py deleted file mode 100644 index 0609105..0000000 --- a/GetData_Mod/DataManager.py +++ /dev/null @@ -1,381 +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 DataFrameHandler_Mod.DFHandler import DataFrameHandler as DFH -from pandas import DataFrame -from PrintMessage_Mod.CloneMessenger import CloneMessenger as CM - - -class DataManager: - #!TODO: Implement Pandas - """[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 not ".git" in url: - url = url.replace('\n','') - 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}" - command = f"git clone {self.NormalizeURL(df[dfHandler.ConfigsJsonValues['GitLink']][i])} {courseStr}//{self.FormatFullnameDate(surnameStr, nameStr)}" - 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/GithubCloner2022.py b/GithubCloner2022.py index 9f097cf..8d04698 100644 --- a/GithubCloner2022.py +++ b/GithubCloner2022.py @@ -1,62 +1,62 @@ # 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 GetData_Mod.DataManager import DataManager as DM - import pandas as pd -from DataFrameHandler_Mod.DFHandler import DataFrameHandler as DfH -##########? Start Basic Configuration ########## +from Modules.DataFrameHandler_Mod.DFHandler import DataFrameHandler as DfH +from Modules.GetData_Mod.DataManager import DataManager as DM + +# ?######### Start Basic Configuration ########## filename = 'Github_Repositories.csv' name = 'Github Repository Cloner' -version = '[V2.0.1]' +version = '[V2.0.11]' author = '[FacuFalcone - CaidevOficial]' -fileConfigName = 'API_Info.json' -##########? End Basic Configuration ########## +fileConfigName = 'Modules/API_Info.json' +# ?######### End Basic Configuration ########## try: - ##########? Start Initialization ########## + # ?#########? Start Initialization ########## JsonFile = pd.read_json(f"./{fileConfigName}", orient='records') JsonAPI = JsonFile['Github'] JsonDFConfigs = JsonFile['DataFrame']['Fields'] - ##########? End Initialization ############ + # ?#########? End Initialization ############ - ##########? Start Objects Instances ########## + # ?#########? Start Objects Instances ########## Handler = DfH() Manager = DM() - ##########? End Objects Instances ########## + # ?#########? End Objects Instances ########## - ##########? Start DataManager Configuration ########## + # ?#########? Start DataManager Configuration ########## Manager.InitialConfig(name, version, author, JsonAPI) - ##########? End DataManager Configuration ########## + # ?#########? End DataManager Configuration ########## - ##########? Start DataFrame Configuration ########## - #* Reads the 'csv' File to get the dataframe + # ?#########? 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 + # *# Sets the Main DF to the class to handle it Handler.MainDataFrame = df Handler.ConfigsJsonValues = JsonDFConfigs Handler.ConfigurateDataFrame(Handler.ConfigsJsonValues['Course']) - ##########? End DataFrame Configuration ########## + # ?#########? End DataFrame Configuration ########## - ##########? Start Initialize DataManager ########## + # ?#########? Start Initialize DataManager ########## Manager.CloneRepositories(Handler) - ###########? End Initialize DataManager ########### + # ?##########? End Initialize DataManager ########### except Exception as e: - print(f'Exception: {e.args}') \ No newline at end of file + print(f'Exception: {e.args}') 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/API_Info.json b/Modules/API_Info.json similarity index 100% rename from API_Info.json rename to Modules/API_Info.json diff --git a/Modules/DataFrameHandler_Mod/DFHandler.py b/Modules/DataFrameHandler_Mod/DFHandler.py new file mode 100644 index 0000000..5fd36c6 --- /dev/null +++ b/Modules/DataFrameHandler_Mod/DFHandler.py @@ -0,0 +1,245 @@ +# 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 numpy import ndarray +from pandas import DataFrame + + +class DataFrameHandler: + """[summary] \n + Class in charge of configurate and handle all the dataframe operations. \n + Returns: + class: [DataFrameHandler]. \n + """ + # ?####? Attributes ##### + __configsJsonValues: dict = {} + __commands = [] + __uniqueColumns: ndarray = [] + __mainDF: DataFrame = None + __studentsDF = [] + __orderedListOfDFStudents: list = [] + # ?####? End Attributes ##### + + def __init__(self) -> None: + pass + + # ?####? Properties ##### + + @property + def OrderListOfDFStudents(self) -> list: + """[summary] \n + Get the list of ordered dataframes. \n + Returns: + [list]: [The list of ordered dataframes]. \n + """ + return self.__orderedListOfDFStudents + + @OrderListOfDFStudents.setter + def OrderListOfDFStudents(self, df: DataFrame) -> None: + """[summary] \n + Set the list of ordered dataframes. \n + Args: + df (DataFrame): [The ordered dataframes]. \n + """ + self.__orderedListOfDFStudents.append(df) + + @property + def ConfigsJsonValues(self) -> dict: + """[summary] \n + Get the configs of the json. \n + Returns: + [dict]: [The configs of the json]. \n + """ + return self.__configsJsonValues + + @ConfigsJsonValues.setter + def ConfigsJsonValues(self, value: dict) -> None: + """[summary] \n + Set the configs of the json. \n + Args: + value (dict): [The configs of the json]. \n + """ + self.__configsJsonValues = value + + @property + def MainDataFrame(self) -> DataFrame: + """[summary] \n + Get the main dataframe. \n + Returns: + [DataFrame]: [The main dataframe]. \n + """ + return self.__mainDF + + @MainDataFrame.setter + def MainDataFrame(self, df: DataFrame): + """[summary] \n + Set the main dataframe. \n + Args: + df (DataFrame): [The dataframe to set]. \n + """ + self.__mainDF = df + + @property + def UniqueColumns(self) -> ndarray: + """[summary] \n + Get the unique values of the column 'columnValue' [Division]. \n + Returns: + [ndarray]: [The unique values of the column 'columnValue' [Division]]. \n + """ + return self.__uniqueColumns + + @UniqueColumns.setter + def UniqueColumns(self, uniqueColumns: ndarray): + """[summary] \n + Set the unique values of the column 'columnValue' [Division]. \n + Args: + uniqueColumns (ndarray): [The unique values of the column 'columnValue' [Division]]. \n + """ + self.__uniqueColumns = uniqueColumns + + @property + def Commands(self) -> list: + """[summary] \n + Get the list of the commands. \n + Returns: + [list]: [The list of the commands]. \n + """ + return self.__commands + + @Commands.setter + def Commands(self, command: str) -> None: + """[summary] \n + Sets a command inside the list of the commands. \n + Args: + command (str): [The command to add]. \n + """ + self.__commands.append(command) + + @property + def StudentsDF(self) -> list: + """[summary] \n + Get the dataframe with the students. \n + Returns: + [DataFrame]: [The dataframe with the students]. \n + """ + return self.__studentsDF + + @StudentsDF.setter + def StudentsDF(self, df: DataFrame) -> None: + """[summary] \n + Set the dataframe with the students. \n + Args: + df (DataFrame): [The dataframe with the students]. \n + """ + self.__studentsDF.append(df) + + # ?####? End Properties ##### + + # ?####? Methods ##### + + def GetSpecificStudentsDF(self, df: DataFrame, column: str, value: str) -> DataFrame: + """[summary] \n + Get the students that have the specified index value in the specified column. \n + The DataFrame MUST be indexed by the 'value' column. \n + Args: + df (DataFrame): [The dataframe to filter]. \n + column (str): [The column to filter]. \n + value (str): [The value to filter]. \n + Returns: + [DataFrame]: [The dataframe with the filtered students ordered by Course, Surname & Name]. \n + """ + specificDF: DataFrame = df[df[column] == value] + orderedDF: DataFrame = self.OrderIndexedDFBy( + specificDF, self.ConfigsJsonValues['Course'], + self.ConfigsJsonValues['Surname'], + self.ConfigsJsonValues['Name'] + ) + return orderedDF + + def CreateListDFStudentsBy(self, df: DataFrame, column: str, columnValue: str): + """[summary] \n + Creates a list of the students that have the specified index value in the specified column. \n + The DataFrame MUST be indexed by the 'value' column. \n + Args: + df (DataFrame): [The dataframe to filter]. \n + column (str): [The column to filter]. \n + value (list): [The values to filter]. \n + """ + self.OrderListOfDFStudents = self.GetSpecificStudentsDF( + df, column, columnValue) + + def OrderIndexedDFBy(self, df: DataFrame, firstField: str, secondField: str, thirdField: str) -> DataFrame: + """[summary] \n + Order the dataframe by the specified fields. \n + Args: + df (DataFrame): [The dataframe to order]. \n + firstField (str): [The first field to order]. \n + secondField (str): [The second field to order]. \n + thirdField (str): [The third field to order]. \n + Returns: + [DataFrame]: [The dataframe ordered by the three fields in the specified order]. \n + """ + sortedDF = df.sort_values( + by=[firstField, secondField, thirdField], ascending=[True, True, True]) + return sortedDF + + def ConfigUniqueValuesInColumn(self, column: str): + """[summary] \n + Get the unique values in the specified column and sort them in alphabetical order ASC. \n + Args: + column (str): [The column to filter]. \n + Returns: + [list]: [The unique values in the specified column]. \n + """ + self.UniqueColumns = self.MainDataFrame[column].unique() + self.UniqueColumns.sort() + + def CreateJsonOfEveryDF(self): + """[summary] \n + Create a json file for every dataframe. \n + """ + + for i in range(len(self.OrderListOfDFStudents)): + frame: DataFrame = self.OrderListOfDFStudents[i] + name = frame.at[frame.index.values[0], + self.ConfigsJsonValues['Course']] + filename: str = f'{name}.json' + frame.to_json(f'{filename}', orient='table', + indent=4, force_ascii=True) + + # ?####? End Methods ##### + + # *####* Main Method ##### + + def ConfigurateDataFrame(self, columnValue: str) -> None: + """[summary] \n + Configurate the dataframe with the specified column value. \n + Args: + columnValue (str): [The column value to configurate]. \n + """ + + # *# 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 unique in self.UniqueColumns: + self.CreateListDFStudentsBy( + self.MainDataFrame, columnValue, unique) + self.CreateJsonOfEveryDF() + + # *####* End Main Method ##### diff --git a/PrintMessage_Mod/__init__.py b/Modules/DataFrameHandler_Mod/__init__.py similarity index 98% rename from PrintMessage_Mod/__init__.py rename to Modules/DataFrameHandler_Mod/__init__.py index 89927cb..7d112a3 100644 --- a/PrintMessage_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 new file mode 100644 index 0000000..3c59b23 --- /dev/null +++ b/Modules/GetData_Mod/DataManager.py @@ -0,0 +1,383 @@ +# 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/DataFrameHandler_Mod/__init__.py b/Modules/GetData_Mod/__init__.py similarity index 98% rename from DataFrameHandler_Mod/__init__.py rename to Modules/GetData_Mod/__init__.py index 89927cb..7d112a3 100644 --- a/DataFrameHandler_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/PrintMessage_Mod/CloneMessenger.py b/Modules/PrintMessage_Mod/CloneMessenger.py similarity index 65% rename from PrintMessage_Mod/CloneMessenger.py rename to Modules/PrintMessage_Mod/CloneMessenger.py index b1e38ba..e3f12dd 100644 --- a/PrintMessage_Mod/CloneMessenger.py +++ b/Modules/PrintMessage_Mod/CloneMessenger.py @@ -1,78 +1,78 @@ # 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 . class CloneMessenger: """[summary] - Class to print messages in the console.\n + Class to print messages in the console. \n Returns: - class: [CloneMessenger].\n + class: [CloneMessenger]. \n """ - #######? START ATTRIBUTES ####### - __message:str = '' - #######? END ATTRIBUTES ####### + # ?######? START ATTRIBUTES ####### + __message: str = '' + # ?######? END ATTRIBUTES ####### def __init__(self) -> None: pass - - def SetMessage(self, message:str) -> None: - """[summary]\n - Sets the message of the class.\n + + 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 + message (str): The message to be printed in the console. \n """ self.__message = message - + def GetMessage(self) -> str: - """[summary]\n - Gets the message of the class.\n + """[summary] \n + Gets the message of the class. \n Returns: - str: Message of the class to be printed in the console.\n + str: Message of the class to be printed in the console. \n """ return self.__message - - ############? START METHODS ############ - def InitializeMessenger(self, message:str) -> None: - """[summary]\n - Initializes the class with a message.\n + # ?###########? START METHODS ############ + + def InitializeMessenger(self, message: str) -> None: + """[summary] \n + Initializes the class with a message. \n Args: - message (str): The message to be printed in the console.\n + message (str): The message to be printed in the console. \n """ self.SetMessage(message) def PrintMessage(self) -> None: - """[summary]\n - Creates a string of symbols of the same length of the message and\n - prints them in the console.\n + """[summary] \n + Creates a string of symbols of the same length of the message and \n + prints them in the console. \n """ symbols = self.GenerateSymbols() print( - '\n', - f'{symbols}\n', - f'{self.GetMessage()}\n', - f'{symbols}\n' + ' \n', + f'{symbols} \n', + f'{self.GetMessage()} \n', + f'{symbols} \n' ) - + def GenerateSymbols(self) -> str: - """[summary]\n - Generates a string of symbols of the same length of the message of the class.\n + """[summary] \n + Generates a string of symbols of the same length of the message of the class. \n Returns: - str: String of symbols of the same length of the message of the class.\n + str: String of symbols of the same length of the message of the class. \n """ return ''.join(['#' for i in range(len(self.GetMessage()))]) - - ############? END METHODS ############ + + # ?###########? END METHODS ############ diff --git a/GetData_Mod/__init__.py b/Modules/PrintMessage_Mod/__init__.py similarity index 98% rename from GetData_Mod/__init__.py rename to Modules/PrintMessage_Mod/__init__.py index 89927cb..7d112a3 100644 --- a/GetData_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 . diff --git a/README.md b/README.md index 1a069c7..2f39c67 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ Like this:


# Configuration -In order to use this Cloner, you should configure the file [apiInfo.json](apiInfo.json) with your Github API's information as shown below. +In order to use this Cloner, you should configure the file [API_Info.json](./Modules/API_Info.json) with your Github API's information as shown below. ```json [