From ee8adeabbc081902c900ebbf763b2d18011cab9b Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Thu, 17 Feb 2022 02:40:06 -0300 Subject: [PATCH 1/3] Update Readme & workflow --- .github/workflows/python-app.yml | 4 ++-- README.md | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index ba53328..40c5795 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -29,8 +29,8 @@ jobs: run: | # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 130 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=130 --statistics - name: Test with pytest run: | pytest diff --git a/README.md b/README.md index cd2b39b..d4d9349 100644 --- a/README.md +++ b/README.md @@ -49,15 +49,17 @@

Watch this little video Demo on 🎥

- + + - +
Youtube Logo
+


From beddbc7d666cc216c0ea8ebbd11f1c952aac49ec Mon Sep 17 00:00:00 2001 From: CaidevOficial Date: Tue, 22 Feb 2022 02:58:47 -0300 Subject: [PATCH 2/3] Updated to V2.1.1 - Plot --- .github/workflows/python-app.yml | 6 +- GithubCloner2022.py | 25 ++- Github_Repositories.csv | 8 +- Media/pieChart.png | Bin 0 -> 45268 bytes Modules/API_Info.json | 5 + Modules/DataManager_Mod/DataManager.py | 30 ++- Modules/DirectoryManager_Mod/DirManager.py | 58 ++++++ Modules/DirectoryManager_Mod/__init__.py | 16 ++ Modules/PlotManager_Mod/PlotManager.py | 224 +++++++++++++++++++++ Modules/PlotManager_Mod/__init__.py | 16 ++ README.md | 40 +++- requirements.txt | Bin 788 -> 1034 bytes 12 files changed, 412 insertions(+), 16 deletions(-) create mode 100644 Media/pieChart.png create mode 100644 Modules/DirectoryManager_Mod/DirManager.py create mode 100644 Modules/DirectoryManager_Mod/__init__.py create mode 100644 Modules/PlotManager_Mod/PlotManager.py create mode 100644 Modules/PlotManager_Mod/__init__.py diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 40c5795..d833ee8 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -31,6 +31,6 @@ jobs: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 130 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=130 --statistics - - name: Test with pytest - run: | - pytest + # - name: Test with pytest + # run: | + # pytest diff --git a/GithubCloner2022.py b/GithubCloner2022.py index 4fef0ca..07b6de6 100644 --- a/GithubCloner2022.py +++ b/GithubCloner2022.py @@ -22,12 +22,14 @@ 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 +from Modules.PlotManager_Mod.PlotManager import PlotManager as Plot +from Modules.DirectoryManager_Mod.DirManager import DirectoryManager as DirM # ?######### Start Basic Configuration ########## filename = 'Github_Repositories.csv' name = 'Github Repository Cloner' -version = '[V2.0.12]' +version = '[V2.1.1]' author = '[FacuFalcone - CaidevOficial]' fileConfigName = 'Modules/API_Info.json' # ?######### End Basic Configuration ########## @@ -40,6 +42,7 @@ JsonFile = pd.read_json(f"./{fileConfigName}", orient='records') JsonAPI = JsonFile['Github'] JsonDFConfigs = JsonFile['DataFrame']['Fields'] + JsonDirConfigs = JsonFile['Files'] # ?#########? End Initialization ############ # ?#########? Start Objects Instances ########## @@ -47,10 +50,20 @@ Manager = DM() Messenger = CM() Timer = FMT() + Plotter = Plot() + DirManager = DirM() # ?#########? End Objects Instances ########## + # ?#########? Start Directory Creation ########## + DirManager.PathToCreate = JsonDirConfigs['Dir_Plots_img'] + DirManager.createDirIfNoExist() + + DirManager.PathToCreate = JsonDirConfigs['Dir_Cloned_Repos'] + DirManager.createDirIfNoExist() + # ?#########? End Directory Creation ########## + # ?#########? Start DataManager Configuration ########## - Manager.InitialConfig(name, version, author, JsonAPI) + Manager.InitialConfig(name, version, author, JsonAPI, JsonDirConfigs['Dir_Cloned_Repos']) # ?#########? End DataManager Configuration ########## # ?#########? Start DataFrame Configuration ########## @@ -66,6 +79,10 @@ # ?#########? Start Initialize DataManager ########## Manager.CloneRepositories(Handler) # ?##########? End Initialize DataManager ########### + + # ?#########? Start PlotManager Configuration ########## + Plotter.initialize(Handler, 'Repositories to Clone', JsonDirConfigs['Dir_Plots_img']) + except Exception as e: print(f'Exception: {e.args}') finally: @@ -79,6 +96,10 @@ Messenger.Message = f"Thanks for using {name} {version} by {author}! ♥" Messenger.PrintMessage() + Messenger.Message = "Creating Pie Chart..." + Messenger.PrintMessage() + Plotter.createPieChart() + Messenger.Message = "Success! All task done. Press a key to close the app" Messenger.PrintMessage() # ?#########? End Print Message ########## diff --git a/Github_Repositories.csv b/Github_Repositories.csv index dee755d..2b14af1 100644 --- a/Github_Repositories.csv +++ b/Github_Repositories.csv @@ -1,12 +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/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","Hades "," Grecian God","1A - Professor 1 - Helper 3","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_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","Artemisa","Grecian God","1H - Professor 3 - Helper 4","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","Odin","Nordic God","1A - Professor 1 - Helper 3","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" +"2022/02/13 10:26:52 p. m. GMT-3","Valhalla","Nordic Reign","1A - Professor 1 - Helper 3","999999","valhalla@nordicreign.com","https://github.com/caidevOficial/Python_RepositoryCloner" diff --git a/Media/pieChart.png b/Media/pieChart.png new file mode 100644 index 0000000000000000000000000000000000000000..6f6af5ae5d3929ea9bd7190fb9e8c6a8f883f90a GIT binary patch literal 45268 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 str: """ return self.__APIDate + @property + def MainDir(self) -> str: + """[summary] \n + Get the main directory to save the downloaded repositories. \n + Returns: + str: [The main directory to save the downloaded repositories]. \n + """ + return self.__mainDir + # ?########? END PROPERTIES - GET ######### # ?########? START PROPERTIES - SET ######### @@ -230,11 +240,20 @@ def APIDate(self, APIResponse: str): date = date[:10] self.__APIDate = date.replace("-", "") + @MainDir.setter + def MainDir(self, mainDir: str) -> None: + """[summary] \n + Set the main directory to save the downloaded repositories. \n + Args: + mainDir (str): [The main directory to save the downloaded repositories]. \n + """ + self.__mainDir = mainDir.strip() + # ?#######? END PROPERTIES - SET ######### # ?########? METHODS ######### - def InitialConfig(self, name: str, version: str, author: str, APIURL: dict): + def InitialConfig(self, name: str, version: str, author: str, APIURL: dict, mainDir: str): """[summary] \n Initialize the config of the class, Also sets the API response \n and the date of the API. \n @@ -243,6 +262,7 @@ def InitialConfig(self, name: str, version: str, author: str, APIURL: dict): 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 + mainDir (str): [The main directory to clone repositories]. \n """ self.AppName = name self.AppVersion = version @@ -250,6 +270,7 @@ def InitialConfig(self, name: str, version: str, author: str, APIURL: dict): self.APIURL = APIURL self.APIResponse = self.APIURL self.APIDate = self.APIResponse + self.MainDir = mainDir def AddComand(self, command: str) -> None: """[summary] \n @@ -315,7 +336,7 @@ def MakeCloneCommands(self, dfHandler: DFH) -> None: git (str): [The url of the git's repository]. \n """ for frame in dfHandler.OrderListOfDFStudents: - self.MakeCloneCommandsForDF(frame, dfHandler) + self.MakeCloneCommandsForDF(frame.reset_index(drop=True), dfHandler) def MakeCloneCommandsForDF(self, df: DataFrame, dfHandler: DFH) -> None: """[summary] \n @@ -323,6 +344,9 @@ def MakeCloneCommandsForDF(self, df: DataFrame, dfHandler: DFH) -> None: Args: df (DataFrame): [The DataFrame with the students information]. \n """ + # *## Deletes the first column [Date] + df = df.drop(columns=dfHandler.ConfigsJsonValues['Date'], inplace=False, axis=1) + df = df.applymap(lambda x: str(x).strip()) for i in df.index: crudeCourse = df[dfHandler.ConfigsJsonValues['Course']][i] courseStr = self.NormalizeCourse(self.FormatCourse(crudeCourse)) @@ -332,7 +356,7 @@ def MakeCloneCommandsForDF(self, df: DataFrame, dfHandler: DFH) -> None: normalizedURL = self.NormalizeURL( df[dfHandler.ConfigsJsonValues['GitLink']][i]) normalizedFullname = self.FormatFullnameDate(surnameStr, nameStr) - command = f"git clone {normalizedURL} {courseStr}//{normalizedFullname}" + command = f"git clone {normalizedURL} {self.MainDir}//{courseStr}//{normalizedFullname}" self.AddComand(command) self.CloningMessages = message diff --git a/Modules/DirectoryManager_Mod/DirManager.py b/Modules/DirectoryManager_Mod/DirManager.py new file mode 100644 index 0000000..6aee1fe --- /dev/null +++ b/Modules/DirectoryManager_Mod/DirManager.py @@ -0,0 +1,58 @@ +# 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 + + +class DirectoryManager: + """[summary] + Class in charge of create directories. \n + Returns: + class: DirectoryManager + """ + # ?#### START ATTRIBUTES ####? + __dirToCreate: str = '' + # ?#### END ATTRIBUTES ####? + + def __init__(self) ->None: + pass + + @property + def PathToCreate(self)-> str: + """[summary] + Gets the path that need to create if not exist. \n + Returns: + str: [Path to create.] + """ + return self.__dirToCreate + + @PathToCreate.setter + def PathToCreate(self, path: str)->None: + """[summary] + Sets the directory that will create. \n + Args: + path (str): [Directory to create.] + """ + self.__dirToCreate = path + + def createDirIfNoExist(self)->None: + """[summary] + Creates the directory if not exist. \n + """ + if not os.path.exists(self.PathToCreate): + os.makedirs(self.PathToCreate) + diff --git a/Modules/DirectoryManager_Mod/__init__.py b/Modules/DirectoryManager_Mod/__init__.py new file mode 100644 index 0000000..7d112a3 --- /dev/null +++ b/Modules/DirectoryManager_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/PlotManager_Mod/PlotManager.py b/Modules/PlotManager_Mod/PlotManager.py new file mode 100644 index 0000000..71a2cfa --- /dev/null +++ b/Modules/PlotManager_Mod/PlotManager.py @@ -0,0 +1,224 @@ +# 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 +import os + +import matplotlib.colors as mcolors +import matplotlib.pyplot as plt +from Modules.DataFrameHandler_Mod.DFHandler import DataFrameHandler + + +class PlotManager: + """[summary] + Class in charge of create the pie chart. \n + Returns: + class: PlotManager + """ + + # ?#### START ATTRIBUTES ####? + __plotDF: DataFrameHandler = None + __labels: list = [] + __slices: list = [] + __colors: list = [] + __title: str = None + __pathToSave: str = None + # ?#### END ATTRIBUTES ####? + + def __init__(self) -> None: + pass + + # ?### START PROPERTIES - GETTERS ###? + + @property + def PathToSave(self) -> str: + """[summary] + Gets the path to save the image of the pie chart. \n + Returns: + str: [The path of the directory to save the image of the pie chart.] + """ + return self.__pathToSave + + @property + def Labels(self) -> list: + """[summary] + Gets the list of labels for the pie chart. \n + Returns: + list: [Labels for the pie Chart] + """ + return self.__labels + + @property + def Slices(self) -> list: + """[summary] + Gets the list of slices with values for every label \n + of the pie chart. \n + Returns: + list: [Slices with values for using in the pie chart] + """ + return self.__slices + + @property + def PlotDF(self) -> DataFrameHandler: + """[summary] + Gets the DataFrameHandler object. \n + Returns: + class: DataFrameHandler + """ + return self.__plotDF + + @property + def Colors(self) -> list: + """[summary] + Gets the list of colors for the pie chart. \n + Returns: + list: [Colors for the pie Chart] + """ + return self.__colors + + @property + def Title(self) -> str: + """[summary] + Gets the title for the legend of the pie chart. \n + Returns: + str: [Title for the legend of the pie chart] + """ + return self.__title + + # ?### END PROPERTIES - GETTERS ###? + + # ?### START PROPERTIES - SETTERS ###? + + @PathToSave.setter + def PathToSave(self, path: str): + """[summary] + Sets the path to save the image of the pie chart. \n + Args: + path (str): [The path of the directory to save the image of the pie chart.] + """ + self.__pathToSave = path.strip() + + @Labels.setter + def Labels(self, value: list): + """[summary] + Sets the list of labels for the pie chart. \n + Args: + value (list): [Labels for using in the pie Chart] + """ + self.__labels = value + + @Slices.setter + def Slices(self, value: list): + """[summary] + Sets the list of slices with values for every label \n + of the pie chart. \n + Args: + value (list): [Slices with values for using in the pie chart] + """ + self.__slices = value + + @PlotDF.setter + def PlotDF(self, value: DataFrameHandler): + """[summary] + Sets the DataFrameHandler object. \n + Args: + value (DataFrameHandler): [DataFrameHandler object] + """ + self.__plotDF = value + + @Colors.setter + def Colors(self, value: list): + """[summary] + Sets the list of colors for the pie chart. \n + Args: + value (list): [Colors for the pie Chart] + """ + self.__colors = value + + @Title.setter + def Title(self, value: str): + """[summary] + Sets the title for the legend of the pie chart. \n + Args: + value (str): [Title for the legend of the pie chart] + """ + self.__title = value.strip() + + # ?### END PROPERTIES - SETTERS ###? + + # ?### START METHODS ###? + + def createColorsList(self) -> list: + """[summary] + Creates a list of colors for the pie chart. \n + Returns: + list: [List of colors for the pie chart] + """ + # Sort colors by hue, saturation, value and name. + by_hsv = sorted((tuple(mcolors.rgb_to_hsv(mcolors.to_rgb(color))), name) + for name, color in mcolors.TABLEAU_COLORS.items()) + names = [name for hsv, name in by_hsv] + return names + + def initialize(self, df: DataFrameHandler, title: str, path: str) -> None: + """[summary] + Initializes the PlotManager object. \n + Args: + df (DataFrameHandler): [DataFrameHandler object] \n + title (str): [Title for the legend of the pie chart] \n + path (str): [Path to save the image of the pie chart] + """ + self.PlotDF = df + self.Labels = self.PlotDF.UniqueColumns + self.Slices = [len(dframe.index) for dframe in self.PlotDF.OrderListOfDFStudents] + self.Title = title + self.PathToSave = path + + def CreateDirToSaveImages(self) -> None: + """[summary] + If not exist, creates a directory to save the images of the pie chart. \n + """ + if not os.path.exists(self.PathToSave): + os.makedirs(self.PathToSave) + + def configureRCparams(self) ->None: + """[summary] + Configures the rcParams of the matplotlib. \n + """ + plt.style.use('seaborn-whitegrid') + plt.rcParams['font.family'] = 'Times New Roman' + plt.rcParams['font.size'] = 12 + plt.rcParams['legend.fontsize'] = 8 + plt.rcParams['lines.linewidth'] = 1.5 + plt.rcParams['lines.markersize'] = 5 + plt.rcParams['lines.markeredgewidth'] = 1 + plt.rcParams['figure.figsize'] = (12, 6) + + def createPieChart(self): + """[summary] + Creates the pie chart. \n + """ + self.configureRCparams() + + plt.pie(self.Slices, labels=self.Labels, startangle=45, shadow=True, + autopct='%1.1f%%') + plt.legend(self.Labels, bbox_to_anchor=(1, 0.5), markerscale=1.2, loc='lower left') + plt.title(self.Title) + plt.tight_layout() + # self.CreateDirToSaveImages() + plt.savefig(f'{self.PathToSave}/{datetime.datetime.today().strftime("%Y%m%d__%H_%M_%S")}.png', dpi=300) + plt.show() diff --git a/Modules/PlotManager_Mod/__init__.py b/Modules/PlotManager_Mod/__init__.py new file mode 100644 index 0000000..7d112a3 --- /dev/null +++ b/Modules/PlotManager_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/README.md b/README.md index d4d9349..4b6037c 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ - + @@ -185,7 +185,7 @@ Meanwhile the program is cloning the repositories, the console will show message

Watch this little video Demo on 🎥

Watch this little video Demo [for version without Pie Chart] on 🎥

- + @@ -200,7 +200,7 @@ When finish, you look a final message (with the elapsed time of the execution) l
Console Messages
Console Messages
- + @@ -211,6 +211,23 @@ When finish, you look a final message (with the elapsed time of the execution) l
Console Final Message
Console Final Message
+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. Additionally, the program will generate a JSON with the data of the students and courses and it will generate a Pie Chart with the percentage of students that have downloaded the repositories... + +Like the image below: + + + + + + + + + + +
Example Pie Chart
+ Example Pie Chart +
+


@@ -252,6 +269,7 @@ In order to use this Cloner, you should configure the file [API_Info.json](./Mod }, "DataFrame": { "Fields": { + "Date": "First_Datetime_Field_To_Delete", "Name": "Name_For_Column_Of_Names", "Surname": "Name_For_Column_Of_Surnames", "Course": "Name_For_Column_Of_Courses", @@ -259,6 +277,10 @@ In order to use this Cloner, you should configure the file [API_Info.json](./Mod "Email": "Name_For_Column_Of_Emails", "GitLink": "Name_For_Column_Of_Links_To_Repositories" } + }, + "Files": { + "Dir_Plots_img": "./DIR_FOR_PLOTS_IMAGES", + "Dir_Cloned_Repos": "./DIR_FOR_CLONED_REPOSITORIES", } ] ``` @@ -275,6 +297,7 @@ for example: }, "DataFrame": { "Fields": { + "Date": "Marca temporal", "Name": "Nombre/s", "Surname": "Apellido/s", "Course": "División", @@ -282,6 +305,10 @@ for example: "Email": "E-Mail", "GitLink": "Link al repositorio" } + }, + "Files": { + "Dir_Plots_img": "./Plot_Images", + "Dir_Cloned_Repos": "./Repositories" } ] ``` @@ -296,14 +323,19 @@ This way the program will take the 'Date' of the last commit of the branch 'main 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.] +Finally, respect the 'Files' Key, where you can configure the directory where the plots will be saved and the directory where the cloned repositories will be saved. + For our example, the columns of the csv file are: - + + diff --git a/requirements.txt b/requirements.txt index 92e4cd1a853d60061dd27ac678f11515734543fe..655d7f3ac839d483fa0849f644f4b73872009d4e 100644 GIT binary patch delta 233 zcmbQj*2OX5m}4?SB||bp4nrzK5rZud8ZhWF7y_Xlkj2Zu#gN93&ydGZ0)+WMn8Q#E zR%HT|H)5~^shxPxQZbt$lc5}li@{3D;D&*WF$O7}_|;lE5oksM(1d&-odZ;y1U3mI zZ8}+%(Ni*+A)6tQAsr}_2P8pmF#@~8cycPEvS Date: Tue, 22 Feb 2022 19:37:23 -0300 Subject: [PATCH 3/3] Upgrade to V2.1.1 --- GithubCloner2022.py | 5 ++--- Github_Repositories.csv | 7 ++++++- Modules/PlotManager_Mod/PlotManager.py | 22 +--------------------- README.md | 2 +- 4 files changed, 10 insertions(+), 26 deletions(-) diff --git a/GithubCloner2022.py b/GithubCloner2022.py index 07b6de6..e11e856 100644 --- a/GithubCloner2022.py +++ b/GithubCloner2022.py @@ -20,11 +20,10 @@ from Modules.DataFrameHandler_Mod.DFHandler import DataFrameHandler as DfH from Modules.DataManager_Mod.DataManager import DataManager as DM +from Modules.DirectoryManager_Mod.DirManager import DirectoryManager as DirM from Modules.Formatter_Mod.Formatter import Formatter as FMT -from Modules.PrintMessage_Mod.CloneMessenger import CloneMessenger as CM from Modules.PlotManager_Mod.PlotManager import PlotManager as Plot -from Modules.DirectoryManager_Mod.DirManager import DirectoryManager as DirM - +from Modules.PrintMessage_Mod.CloneMessenger import CloneMessenger as CM # ?######### Start Basic Configuration ########## filename = 'Github_Repositories.csv' diff --git a/Github_Repositories.csv b/Github_Repositories.csv index 2b14af1..8eeb14d 100644 --- a/Github_Repositories.csv +++ b/Github_Repositories.csv @@ -9,4 +9,9 @@ "2022/02/13 10:26:52 p. m. GMT-3","Odin","Nordic God","1A - Professor 1 - Helper 3","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","1A - Professor 1 - Helper 3","999999","valhalla@nordicreign.com","https://github.com/caidevOficial/Python_RepositoryCloner" +"2022/02/13 10:26:52 p. m. GMT-3","Valhalla","Nordic Reign","1A - Professor 1 - Helper 3","999999","valhalla@nordicreign.com","https://github.com/caidevOficial/CaidevOficial" +"2022/02/13 10:26:52 p. m. GMT-3","Medusa","Grecian Monster","1C - Professor 5 - Helper 5","999999","Medusa@nordicreign.com","https://github.com/caidevOficial/CaidevOficial" +"2022/02/13 10:26:52 p. m. GMT-3","Heracles","Grecian DemiGod","1A - Professor 1 - Helper 3","999999","Heracles@nordicreign.com","https://github.com/caidevOficial/CaidevOficial" +"2022/02/13 10:26:52 p. m. GMT-3","Prometeus","Grecian Giant","1B - Professor 4 - Helper 1","999999","Prometeus@nordicreign.com","https://github.com/caidevOficial/CaidevOficial" +"2022/02/13 10:26:52 p. m. GMT-3","Cronos","Grecian Titan","1C - Professor 5 - Helper 5","999999","Cronos@nordicreign.com","https://github.com/caidevOficial/CaidevOficial" +"2022/02/13 10:26:52 p. m. GMT-3","GEA","Grecian Titan","1H - Professor 3 - Helper 4","999999","GEA@nordicreign.com","https://github.com/caidevOficial/CaidevOficial" diff --git a/Modules/PlotManager_Mod/PlotManager.py b/Modules/PlotManager_Mod/PlotManager.py index 71a2cfa..14be034 100644 --- a/Modules/PlotManager_Mod/PlotManager.py +++ b/Modules/PlotManager_Mod/PlotManager.py @@ -162,18 +162,6 @@ def Title(self, value: str): # ?### START METHODS ###? - def createColorsList(self) -> list: - """[summary] - Creates a list of colors for the pie chart. \n - Returns: - list: [List of colors for the pie chart] - """ - # Sort colors by hue, saturation, value and name. - by_hsv = sorted((tuple(mcolors.rgb_to_hsv(mcolors.to_rgb(color))), name) - for name, color in mcolors.TABLEAU_COLORS.items()) - names = [name for hsv, name in by_hsv] - return names - def initialize(self, df: DataFrameHandler, title: str, path: str) -> None: """[summary] Initializes the PlotManager object. \n @@ -188,13 +176,6 @@ def initialize(self, df: DataFrameHandler, title: str, path: str) -> None: self.Title = title self.PathToSave = path - def CreateDirToSaveImages(self) -> None: - """[summary] - If not exist, creates a directory to save the images of the pie chart. \n - """ - if not os.path.exists(self.PathToSave): - os.makedirs(self.PathToSave) - def configureRCparams(self) ->None: """[summary] Configures the rcParams of the matplotlib. \n @@ -216,9 +197,8 @@ def createPieChart(self): plt.pie(self.Slices, labels=self.Labels, startangle=45, shadow=True, autopct='%1.1f%%') - plt.legend(self.Labels, bbox_to_anchor=(1, 0.5), markerscale=1.2, loc='lower left') + plt.legend(self.Labels, bbox_to_anchor=(1, 0.5), markerscale=1.2, loc='upper left') plt.title(self.Title) plt.tight_layout() - # self.CreateDirToSaveImages() plt.savefig(f'{self.PathToSave}/{datetime.datetime.today().strftime("%Y%m%d__%H_%M_%S")}.png', dpi=300) plt.show() diff --git a/README.md b/README.md index 4b6037c..bdb8a2e 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@
Nombre/sApellido/sDivisiónDNI / LegajoE-MailLink al repositorioMarca TemporalNombre/sApellido/sDivisiónDNI / LegajoE-MailLink al repositorio
+

2022/02/13 10:26:52 p. m. GMT-3

+

Poseidon

- +
Youtube Logo