diff --git a/API_Info.json b/API_Info.json new file mode 100644 index 0000000..8b92de3 --- /dev/null +++ b/API_Info.json @@ -0,0 +1,18 @@ +{ + "Github": { + "URL": "https://api.github.com/repos", + "USER": "CaidevOficial", + "REPO": "Python_Udemy_DataManipulation", + "BRANCH": "main" + }, + "DataFrame": { + "Fields": { + "Name": "Nombre/s", + "Surname": "Apellido/s", + "Course": "División", + "ID": "DNI / Legajo", + "Email": "E-Mail", + "GitLink": "Link al repositorio" + } + } +} \ No newline at end of file diff --git a/DataFrameHandler_Mod/DFHandler.py b/DataFrameHandler_Mod/DFHandler.py new file mode 100644 index 0000000..3e1cd89 --- /dev/null +++ b/DataFrameHandler_Mod/DFHandler.py @@ -0,0 +1,238 @@ +# 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/DataFrameHandler_Mod/__init__.py b/DataFrameHandler_Mod/__init__.py new file mode 100644 index 0000000..89927cb --- /dev/null +++ b/DataFrameHandler_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/GetData_Mod/DataManager.py b/GetData_Mod/DataManager.py index 55f17fc..0609105 100644 --- a/GetData_Mod/DataManager.py +++ b/GetData_Mod/DataManager.py @@ -16,173 +16,263 @@ # 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 -#import pandas as pd -#from pandas import DataFrame as df - class DataManager: #!TODO: Implement Pandas - """[summary] - Class in charge of the file management to read and process the - data of the students in order to clone their repositorys. + """[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] + [class]: [DataManager].\n """ + #########? START ATTRIBUTES ######### __configAPIURL = '' __name = '' __version = '' + __author = '' __commands: list = [] - __Messenger: CM = None - #__studentsInfo: pd.DataFrame = pd.DataFrame() + __Messenger: CM = CM() + __studentsInfo: DataFrame = DataFrame() + __APIResponse = None + __cloningMessages:list = [] + ########? END ATTRIBUTES ######### - def __init__(self, fileName:str, name:str, version:str, APIURL:str): - self.SetFilename(fileName) + 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] - Set the name of the file to read + """[summary]\n + Set the name of the file to read.\n Args: - fileName (str): [The name of the file to read] + fileName (str): [The name of the file to read].\n """ self.fileName = fileName def SetAppName(self, name:str)->None: - """[summary] - Set the name of the file + """[summary]\n + Set the name of the file.\n Args: - name (str): [The name of the file] + name (str): [The name of the file].\n """ self.__name = name def SetAppVersion(self, version:str)->None: - """[summary] - Set the version of the file + """[summary]\n + Set the version of the file.\n Args: - version (str): [The version of the file] + version (str): [The version of the file].\n """ self.__version = version - def SetAPIURL(self, api:str)->None: - """[summary] - Set the URL of the API - Args: - api (str): [The URL of the API] + 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 = api + self.__configAPIURL = f'{api["URL"]}/{api["USER"]}/{api["REPO"]}/commits/{api["BRANCH"]}' def AddComand(self, command:str)->None: - """[summary] - Add a command to the list of the commands + """[summary]\n + Add a command to the list of the commands.\n Args: - command (str): [The command to add] + 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] - Get the commands to clone the repositories of the students + """[summary]\n + Get the commands to clone the repositories of the students.\n Returns: - list: [The list of the commands to execute] + list: [The list of the commands to execute].\n """ return self.__commands - + def GetAppName(self)->str: - """[summary] - Get the name of the application + """[summary]\n + Get the name of the application.\n Returns: - str: [The name of the application] + str: [The name of the application].\n """ return self.__name def GetAppVersion(self)->str: - """[summary] - Get the version of the application + """[summary]\n + Get the version of the application.\n Returns: - str: [The version of the application] + str: [The version of the application].\n """ return self.__version def GetFilename(self)->str: - """[summary] - Get the name of the file + """[summary]\n + Get the name of the file.\n Returns: - str: [The name of the file] + str: [The name of the file].\n """ return self.fileName def GetAPIURL(self)->str: - """[summary] - Get the URL of the API + """[summary]\n + Get the URL of the API.\n Returns: - str: [The URL of the API] + str: [The URL of the API].\n """ return self.__configAPIURL - def GetDate(self, api:str)->str: - """[summary] - Get the date from the API - Args: - api (str): [The link of the API to consume] + def GetDate(self)->str: + """[summary]\n + Get the date from the API.\n Returns: - str: [The date formatted without the dashes] + str: [The date formatted without the dashes].\n """ - date = requests.get(api) - date = date.json()["commit"]["author"]["date"] + date = self.APIResponse.json()["commit"]["author"]["date"] date = date[:10] return date.replace("-","") - #!TODO: Implement Pandas - def OpenFile(self)->None: - """[summary] - Open the file and get the data + 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 """ - #file = pd.read_csv(self.fileName) - appInfo = f'{self.GetAppName()} - {self.GetAppVersion()}' - self.__Messenger = CM(appInfo) - self.__Messenger.PrintMessage() + return self.__studentsInfo - try: - with open(self.GetFilename(), 'r') as f: - lines:list = f.readlines()[1::] - for line in lines: - fieldList = line.split(',') - fieldList = [x.strip().replace('"', '') for x in fieldList] - - if len(fieldList)>=6: - message = f'Cloning repository of {fieldList[2]}, {fieldList[1]}' - self.__Messenger = CM(message) - studentGitUrl = fieldList[6] - api = self.GetAPIURL() - date = self.GetDate(api) - - self.__Messenger.PrintMessage() - - self.MakeCloneCommands( - self.FormatSurname(fieldList, date), - self.FormatCourse(fieldList), - self.NormalizeURL(studentGitUrl) - ) - self.ExecuteCommands(self.GetCommands()) - self.__Messenger.SetMessage('All Repositories have been cloned!') - self.__Messenger.PrintMessage() - except Exception as e: - self.__Messenger = CM(e) - self.__Messenger.PrintMessage() + #########? 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] - Normalize the URLs of the git's repositorys of the students, - adding the .git at the end if it is not already there + """[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] + url (str): [The crudal url of the git's repository].\n Returns: - str: [The normalized url] + str: [The normalized url].\n """ if not ".git" in url: url = url.replace('\n','') @@ -190,56 +280,102 @@ def NormalizeURL(self, url:str)->str: return url.replace("\\n","") def NormalizeCourse(self, course:str)->str: - """[summary] - Normalize the course name, removing the spaces. + """[summary]\n + Normalize the course name, removing the spaces.\n Args: - course (str): [The course name] + course (str): [The course name].\n Returns: - str: [The normalized course name] + str: [The normalized course name].\n """ return course.replace(' - ', '-').replace(" ","_") - def FormatSurname(self, fieldList:list, date)->str: - """[summary] - Format the surname of the student, removing the spaces and replacing them with _ + 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: - fieldList (list): [The list of the fields of the student] - date (str): [The date of the student] + 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 surname] + str: [The formatted Fullname like this: surname_name_date].\n """ - surname = fieldList[2].replace(",","_").replace(" ","").replace("\n","") - name = fieldList[1].replace(",","_").replace(" ","").replace("\n","") + surname = surname.replace(",","_").replace(" ","").replace("\n","") + name = name.replace(",","_").replace(" ","").replace("\n","") - return f'{surname}_{name}_{date}' + return f'{surname}_{name}_{self.GetDate()}' def FormatCourse(self, fieldList:str)->str: - """[summary] - Format the course of the student, removing the line jumps and replacing them with _ + """[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] + fieldList (str): [The field of the course. It is the second field of the csv file].\n Returns: - str: [The formatted course] + str: [The formatted course].\n """ - return self.NormalizeCourse(fieldList[3].replace("\n","")) - - def MakeCloneCommands(self, surname:str, course:str, git:str)->None: - """[summary] - Make the commands to clone the repositories of the students + 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: - surname (str): [The surname of the student] - course (str): [The course of the student] - git (str): [The url of the git's repository] + df (DataFrame): [The DataFrame with the students information].\n """ - command = f'git clone {git} {course}//{surname}' - self.AddComand(command) + 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, commandList:list)->None: - """[summary] - Execute the commands to clone the repositories of the students + 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] + commandList (list): [The list of the commands to execute].\n """ - commandList = [x.strip() for x in commandList] + + 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 5130557..9f097cf 100644 --- a/GithubCloner2022.py +++ b/GithubCloner2022.py @@ -15,24 +15,48 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import json from GetData_Mod.DataManager import DataManager as DM -########## Start Basic Configuration ########## +import pandas as pd +from DataFrameHandler_Mod.DFHandler import DataFrameHandler as DfH + +##########? Start Basic Configuration ########## filename = 'Github_Repositories.csv' name = 'Github Repository Cloner' -version = '[V1.1.05]' -fileConfigName = 'apiInfo.json' -########## End Basic Configuration ########## +version = '[V2.0.1]' +author = '[FacuFalcone - CaidevOficial]' +fileConfigName = 'API_Info.json' +##########? End Basic Configuration ########## try: - with open(fileConfigName, 'r') as APIFILE: - JsonFile = json.load(APIFILE)[0] - print(JsonFile) - APIURL = f'{JsonFile["URL"]}/{JsonFile["USER"]}/{JsonFile["REPO"]}/commits/{JsonFile["BRANCH"]}' - print(APIURL) - - manager = DM(filename, name, version, APIURL) - manager.OpenFile() -except FileNotFoundError: - print(f'File not found: {fileConfigName}') + ##########? 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}') \ No newline at end of file diff --git a/Github_Repositories.csv b/Github_Repositories.csv index 5ed0f49..ce50003 100644 --- a/Github_Repositories.csv +++ b/Github_Repositories.csv @@ -1,5 +1,7 @@ "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","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","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","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" +"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" diff --git a/Media/Directories_tree.png b/Media/Directories_tree.png new file mode 100644 index 0000000..b137e30 Binary files /dev/null and b/Media/Directories_tree.png differ diff --git a/Media/Messages.png b/Media/Messages.png new file mode 100644 index 0000000..219d29a Binary files /dev/null and b/Media/Messages.png differ diff --git a/PrintMessage_Mod/CloneMessenger.py b/PrintMessage_Mod/CloneMessenger.py index cd98fc4..b1e38ba 100644 --- a/PrintMessage_Mod/CloneMessenger.py +++ b/PrintMessage_Mod/CloneMessenger.py @@ -15,39 +15,49 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import logging - class CloneMessenger: - """_summary_ - Class to print messages in the console + """[summary] + Class to print messages in the console.\n Returns: - class: CloneMessenger + class: [CloneMessenger].\n """ + #######? START ATTRIBUTES ####### __message:str = '' + #######? END ATTRIBUTES ####### - def __init__(self, message:str) -> None: - self.SetMessage(message) + def __init__(self) -> None: + pass def SetMessage(self, message:str) -> None: - """[_summary] - Sets the message of the class + """[summary]\n + Sets the message of the class.\n Args: - message (str): The message to be printed in the console + message (str): The message to be printed in the console.\n """ self.__message = message def GetMessage(self) -> str: - """_summary_ - Gets the message of the class + """[summary]\n + Gets the message of the class.\n Returns: - str: Message of the class to be printed in the console. + 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 + Args: + message (str): The message to be printed in the console.\n + """ + self.SetMessage(message) + def PrintMessage(self) -> None: - """[summary] - Creates a string of symbols of the same length of the message and - prints them in the console. + """[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( @@ -58,10 +68,11 @@ def PrintMessage(self) -> None: ) def GenerateSymbols(self) -> str: - """[summary] - Generates a string of symbols of the same length of the message of the class + """[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 + 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 ############ diff --git a/README.md b/README.md index da02e7e..345853c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,176 @@ + + + + + +
+ Universidad Tecnológica Nacional + + Python Logo +
+
+
+

Pisces♓ | Developer👨‍💻 | Pythonpython | GCP GCP | Java java | C# csharp | Dreamer 💖 | Teacher👨‍🏫| A bit nerd🤓

+
+

📌 Programming Student & Assistant Professor at the
+ National Technological University [UTN] 👨‍💻

+

📌 Backend programmer at Accenture 👨‍💻

+
+ +![](https://hit.yhype.me/github/profile?user_id=12877139) + +

+ caidevoficial +

+ +

+ + caidevoficial + +

+


+ +--- + +


# Repository Cloner -It allows you to clone repositories from github in bulk and store them in specific directories from a csv file. +The program it allows you to clone repositories from github in bulk and store them in specific directories from a csv file. +Aditionally it saves the data of every student & course into a json with the name of the course. + +All this is possible by the use of [Pandas library](https://pandas.pydata.org/docs/index.html) and dataframes to manipulate the data, sorting and filtering the courses, students and repositories to get a list of dataframes, one for each course with all the students data sorted by course, surname and name. + +At the end of the execution, the program will download the files of every student and save them in the directory of the course that they belong to. + +like this: + + + + + + + + + + +
Courses & students directories
+ Directories Image +
+ +And the JSON generates with the data of the students and courses will be like this: + +```json +{ + "schema":{ + "fields":[ + { + "name":"index", + "type":"integer" + }, + { + "name":"Marca temporal", + "type":"string" + }, + { + "name":"Nombre\/s", + "type":"string" + }, + { + "name":"Apellido\/s", + "type":"string" + }, + { + "name":"Divisi\u00f3n", + "type":"string" + }, + { + "name":"DNI \/ Legajo", + "type":"integer" + }, + { + "name":"E-Mail", + "type":"string" + }, + { + "name":"Link al repositorio", + "type":"string" + } + ], + "primaryKey":[ + "index" + ], + "pandas_version":"1.4.0" + }, + "data":[ + { + "index":5, + "Marca temporal":"2022\/02\/13 10:26:52 p.\u00a0m. GMT-3", + "Nombre\/s":"Artemisa", + "Apellido\/s":"Grecian God", + "Divisi\u00f3n":"1G - Professor 1 - Helper 1", + "DNI \/ Legajo":444444, + "E-Mail":"zeus@ray.com", + "Link al repositorio":"https:\/\/github.com\/caidevOficial\/Python_IEEE_Team14293.git" + }, + { + "index":3, + "Marca temporal":"2022\/02\/13 10:26:52 p.\u00a0m. GMT-3", + "Nombre\/s":"Zeus", + "Apellido\/s":"Grecian God", + "Divisi\u00f3n":"1G - Professor 1 - Helper 1", + "DNI \/ Legajo":444444, + "E-Mail":"zeus@ray.com", + "Link al repositorio":"https:\/\/github.com\/caidevOficial\/Python_IEEE_Team14293.git" + }, + { + "index":4, + "Marca temporal":"2022\/02\/13 10:26:52 p.\u00a0m. GMT-3", + "Nombre\/s":"Mercury", + "Apellido\/s":"Romane God", + "Divisi\u00f3n":"1G - Professor 1 - Helper 1", + "DNI \/ Legajo":222222, + "E-Mail":"neptune@notplanet.com", + "Link al repositorio":"https:\/\/github.com\/caidevOficial\/SPD2022_TPS.git" + }, + { + "index":0, + "Marca temporal":"2022\/02\/13 10:26:52 p.\u00a0m. GMT-3", + "Nombre\/s":"Neptune", + "Apellido\/s":"Romane God", + "Divisi\u00f3n":"1G - Professor 1 - Helper 1", + "DNI \/ Legajo":222222, + "E-Mail":"neptune@notplanet.com", + "Link al repositorio":"https:\/\/github.com\/caidevOficial\/SPD2022_TPS.git" + } + ] +} +``` +


+ +--- + +


+# Console Messages +Meanwhile the program is cloning the repositories, the console will show messages like showns below: + + + + + + + + + +
Console Messages
+ Console Messages Image +
+ + +


+ +--- + +


### File format * 1st Column: Date time. [it isn't used yet.] * 2nd Column: Student Name @@ -10,7 +180,19 @@ It allows you to clone repositories from github in bulk and store them in specif * 6th Column: Student E-mail. [it isn't used yet.] * 7th Column: Repository Name to download (It could skip the '.git' part) +Like this: + +``` +"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","Hades","Grecian God","1F - Professor 2 - Helper 2","111111","Hades@underworld.com","https://github.com/caidevOficial/CaidevOficial.git" +``` +


+ +--- +


# 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. @@ -21,6 +203,16 @@ In order to use this Cloner, you should configure the file [apiInfo.json](apiInf "USER": "Your_Github_User", "REPO": "Your_Repository_To_Get_The_Date_Of_Last_Commit", "BRANCH": "The_Branch_Of_The_Last_Commit_lowercase" + }, + "DataFrame": { + "Fields": { + "Name": "Name_For_Column_Of_Names", + "Surname": "Name_For_Column_Of_Surnames", + "Course": "Name_For_Column_Of_Courses", + "ID": "Name_For_Column_Of_Students_ID", + "Email": "Name_For_Column_Of_Emails", + "GitLink": "Name_For_Column_Of_Links_To_Repositories" + } } ] ``` @@ -34,6 +226,16 @@ for example: "USER": "CaidevOficial", "REPO": "Python_Udemy_DataManipulation", "BRANCH": "main" + }, + "DataFrame": { + "Fields": { + "Name": "Nombre/s", + "Surname": "Apellido/s", + "Course": "División", + "ID": "DNI / Legajo", + "Email": "E-Mail", + "GitLink": "Link al repositorio" + } } ] ``` @@ -46,7 +248,80 @@ https://api.github.com/repos/CaidevOficial/Python_Udemy_DataManipulation/commits 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. +Regarding the 'DataFrame' Key, al the keys inside are configured to use them with a 'csv' file with at least theses columns. [Could have more columns, but it's not necessary for us.] + +For our example, the columns of the csv file are: + + + + + + + + + + + + + + +
Nombre/sApellido/sDivisiónDNI / LegajoE-MailLink al repositorio
+

Poseidon

+
+

Grecian God

+
+

1F

+
+

123456789

+
+

poseidon@grecianGod.olympus

+
+

https://github.com/caidevOficial/CaidevOficial.git

+
+


+ +--- + +


+ + +

Technologies used. 📌

+ + + + + + + + + + + + + + + + + + + +
+ Pyhton Logo +
Python
+ Pandas Logo +
Pandas
+ NumPy Logo +
Numpy
+ MatPlotLib Logo +
MatPlotLib
+ VSCode Logo +
VSCode
+


+ +--- + +


@@ -96,3 +371,49 @@ This way the program will take the 'Date' of the last commit of the branch 'main
+


+ +--- + +


+ + + + + + + + + + + + + + + + + + + + + + +

Where to find me: 🌎

+ Facu +
🤴 Facu Falcone - Junior Developer
+ + GitHub + +
+ + LinkedIn + +
+ + Invitame un café en cafecito.app + +
+ + Buy Me a Coffee at ko-fi.com + +
\ No newline at end of file diff --git a/apiInfo.json b/apiInfo.json deleted file mode 100644 index fc1e810..0000000 --- a/apiInfo.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "URL": "https://api.github.com/repos", - "USER": "CaidevOficial", - "REPO": "Python_Udemy_DataManipulation", - "BRANCH": "main" - } -] \ No newline at end of file