├── .gitignore ├── LICENSE ├── README.md ├── config.ini ├── images └── usage-preview.png ├── main.py ├── models ├── js.txt ├── py.txt └── ts.txt ├── modules ├── autodoc.py ├── divider.py ├── file.py ├── prompt.py ├── result.py └── settings.py ├── requirements.txt └── results ├── js └── example_commented.js ├── python ├── autodoc_commented.py ├── divider_commented.py ├── file_commented.py ├── prompt_commented.py ├── result_commented.py └── settings_commented.py └── ts ├── example_2_commented.ts └── example_commented.ts /.gitignore: -------------------------------------------------------------------------------- 1 | *__pycache__ 2 | /venv 3 | 4 | /.vscode 5 | /token.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 awekrx 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoDoc-ChatGPT 2 | 3 | AutoDoc-ChatGPT is a Python script that leverages the power of ChatGPT model to automatically generate documentation for any programming language. 4 | 5 | With AutoDoc-ChatGPT, you can easily generate comprehensive documentation for your codebase. Simply provide the path to file you wish to document, and AutoDoc-ChatGPT will generate a detailed description of its functionality, parameters, and return values in natural language. 6 | 7 | ## Getting Started 8 | 9 | ### Installing 10 | 11 | Clone this repository to your local machine: 12 | 13 | ```bash 14 | git clone https://github.com/awekrx/AutoDoc-ChatGPT.git 15 | ``` 16 | 17 | Then install the required packages: 18 | 19 | ```bash 20 | pip install -r requirements.txt 21 | ``` 22 | 23 | ## Supported programming languages 24 | 25 | - [X] Python 26 | - [X] JavaScript 27 | - [X] TypeScript 28 | - [ ] Go 29 | - [ ] C# 30 | - [ ] C++ 31 | - [ ] PHP 32 | - [ ] Ruby 33 | - [ ] Rust 34 | - [ ] Java 35 | - [ ] Kotlin 36 | 37 | And others... 38 | 39 | > `disclaimer`: TypeScript and JavScript has some bugs and is under testing 40 | > 41 | > `disclaimer_2.0`: There is a known error commenting files with 2 or more classes that have methods with the same name. 42 | > 43 | 44 | ## Usage 45 | 46 | ### Edit config 47 | 48 | Open `config.ini` and add `email` and `password` there if you are not using OAuth authorization. 49 | Otherwise open [ChatGPT](https://chat.openai.com) and get the `__Secure-next-auth.session-token` cookie and write it to the `session-token`. 50 | 51 | ### Start 52 | 53 | ```bash 54 | py main.py -file "path to the file" 55 | ``` 56 | 57 | The path to the file can be either relative or absolute. 58 | 59 | After execution, the file `yourfilename_commented.language` is created in the folder with the desired file. 60 | 61 | OR 62 | 63 | ```bash 64 | py main.py -file "path to the file" 65 | ``` 66 | 67 | __Not recommended, little tested.__ 68 | You can specify a file with a comment option to create such comments. 69 | 70 | ### Usage example 71 | 72 | ![Usage preview](./images/usage-preview.png) 73 | 74 | ## Alternative usage 75 | 76 | You can also use this as a function in python. 77 | 78 | ```python 79 | from modules.autodoc import AutoDoc 80 | import configparser 81 | 82 | config = configparser.ConfigParser() 83 | config.read("config.ini") 84 | 85 | auth = { 86 | "email": config["ChatGPT"]["email"], 87 | "password": config["ChatGPT"]["password"], 88 | "session_token": config["ChatGPT"]["session_token"] 89 | } 90 | 91 | result = AutoDoc( 92 | auth, 93 | "Code for commenting", 94 | "language code", 95 | "example comment model (optional)" 96 | ).start() 97 | print(result) 98 | # out: Code with comments 99 | ``` 100 | 101 | ## Disclaimer 102 | 103 | Doesn't always create correct comments. It doesn't always mean what you want. Use as a draft of comments that may need to be edited. 104 | 105 | ## License 106 | 107 | This project is licensed under the MIT License. 108 | 109 | ## Acknowledgments 110 | 111 | Thanks a lot to the development of AI and separately to [СhatGPT](https://chat.openai.com) for generating the Readme. 112 | 113 | And also [acheong08](https://github.com/acheong08) for creating [ChatGPT](https://github.com/acheong08/ChatGPT). 114 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [ChatGPT] 2 | email= 3 | password= 4 | session_token= -------------------------------------------------------------------------------- /images/usage-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awekrx/AutoDoc-ChatGPT/2ae010d1f67fd55b3d3ba8f72e5761174d9a81e3/images/usage-preview.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | from modules.autodoc import AutoDoc 4 | from modules.file import File 5 | from modules.settings import * 6 | import configparser 7 | 8 | config = configparser.ConfigParser() 9 | config.read("config.ini") 10 | 11 | auth = { 12 | "email": config["ChatGPT"]["email"], 13 | "password": config["ChatGPT"]["password"], 14 | "session_token": config["ChatGPT"]["session_token"], 15 | } 16 | 17 | 18 | parser = argparse.ArgumentParser( 19 | description="AutoDoc is a console application for generating code documentation using ChatGPT. Currently supports 1 language: Python. You can use an example file for any language using -example." 20 | ) 21 | 22 | 23 | parser.add_argument("-file", type=str, help="Path to the code file.", required=True) 24 | 25 | args = parser.parse_args() 26 | 27 | 28 | if not os.path.exists(args.file): 29 | exit(f"[{RED}Error{RESET}] Code file does not exist") 30 | 31 | 32 | file = File(args.file) 33 | 34 | autodoc = AutoDoc( 35 | auth, 36 | file.content(), 37 | file.language(), 38 | ) 39 | 40 | result = autodoc.start() 41 | file.create_commented_file(result) 42 | -------------------------------------------------------------------------------- /models/js.txt: -------------------------------------------------------------------------------- 1 | Write JSDoc documentations all classes, constructors, functions() for code (Comments need to be written only for the code that I threw it off | Do not add any notes from yourself | Variays: let, const. Functional parts of the code: if, else, for, and so on, you do not need to comment | Comments for everything that is not a class or function initialization are not needed, that is, for variables that are behind the class or use of functions, you do not need to comment): 2 | 3 | What not to comment: 4 | const test = 1 5 | read() 6 | if(x > 2) 7 | and etc 8 | 9 | CODE 10 | 11 | Write your answer in the format: 12 | |class/constructor/function Name| 13 | Comment: ... 14 | 15 | Example: 16 | From code: 17 | class Prototype { 18 | primitive: any; 19 | component: object; 20 | circularReference: ComponentWithBackReference; 21 | dots: [ 22 | Dot, 23 | Dot, 24 | Dot?, 25 | Dot?, 26 | Dot?, 27 | Dot?, 28 | Dot?, 29 | Dot?, 30 | Dot?, 31 | Dot?, 32 | ]; 33 | 34 | constructor(){ 35 | 36 | } 37 | 38 | clone(): this { 39 | const clone = Object.create(this); 40 | 41 | clone.component = Object.create(this.component); 42 | clone.circularReference = { 43 | ...this.circularReference, 44 | prototype: { ...this }, 45 | }; 46 | 47 | return clone; 48 | } 49 | } 50 | 51 | Rectangle implements Shape { 52 | constructor(protected readonly width: number, protected readonly height: number) { } 53 | 54 | getArea(): number { 55 | return this.widththis.height; 56 | } 57 | 58 | toString(): string { 59 | return `Rectangle[width=${this.width}, height=${this.height}]`; 60 | } 61 | } 62 | 63 | Created comments: 64 | |class Prototype| 65 | Comment: 66 | This class represents a prototype object that can be cloned. 67 | @class 68 | 69 | 70 | |primitive| 71 | Comment: 72 | A property that can hold any primitive value. 73 | @type {any} 74 | 75 | 76 | |component| 77 | Comment: 78 | A property that holds an object. 79 | @type {object} 80 | 81 | 82 | |circularReference| 83 | Comment: 84 | A property that holds a reference to another object of type ComponentWithBackReference. 85 | @type {ComponentWithBackReference} 86 | 87 | 88 | |dots| 89 | Comment: 90 | A property that holds the dots of the prototype. 91 | @type {[Dot, Dot, Dot?, Dot?, Dot?, Dot?, Dot?, Dot?, Dot?, Dot?]} 92 | 93 | 94 | |constructor| 95 | Comment: 96 | Creates a new Prototype instance. 97 | @constructor 98 | 99 | 100 | |clone| 101 | Comment: 102 | Returns the clone of the object. 103 | @returns {this} 104 | 105 | 106 | |class Rectangle| 107 | Comment: 108 | This class represents a rectangle shape and implements the Shape interface. 109 | @implements {Shape} 110 | @class 111 | 112 | 113 | |constructor| 114 | Comment: 115 | Creates a new Rectangle instance with the given width and height. 116 | @constructor 117 | @param {number} width - The width of the rectangle. 118 | @param {number} height - The height of the rectangle. 119 | 120 | 121 | |getArea| 122 | Comment: 123 | Calculates and returns the area of the rectangle. 124 | @returns {number} - The area of the rectangle. 125 | 126 | 127 | |toString| 128 | Comment: 129 | Returns a string representation of the rectangle. 130 | @returns {string} - The string representation of the rectangle. -------------------------------------------------------------------------------- /models/py.txt: -------------------------------------------------------------------------------- 1 | Write Google Style documentations all classes and functions (If there are no functions, then variables do not need to be commented) for code: 2 | 3 | CODE 4 | 5 | Write your answer in the format: 6 | |class/def Name| 7 | Description: ... 8 | Arguments: ... 9 | Returns: ... 10 | 11 | Example: 12 | |class Splitter| 13 | Description: This class takes a code string and a language string and splits the code into sections based on language-specific separators. 14 | 15 | Arguments: 16 | 17 | code (str): A string of code to be split 18 | language (str): A string representing the language of the code (e.g. "py" for Python) 19 | |def __init__| 20 | Description: Initializes a Splitter instance with the given code and language. 21 | 22 | Arguments: 23 | 24 | code (str): A string of code to be split 25 | language (str): A string representing the language of the code (e.g. "py" for Python) 26 | |def split| 27 | Description: Splits the code into sections based on language-specific separators. 28 | 29 | Arguments: None 30 | 31 | Returns: 32 | 33 | list[str]: A list of strings representing the sections of the code. 34 | |def py| 35 | Description: A method that splits Python code into sections based on class and function definitions. 36 | 37 | Arguments: None 38 | 39 | Returns: 40 | 41 | list: A list of strings representing the sections of the Python code. -------------------------------------------------------------------------------- /models/ts.txt: -------------------------------------------------------------------------------- 1 | Write JSDoc documentations all classes, constructors, interfaces, types, functions(public/private/protected) and variables(public/private/protected) for code (Comments need to be written only for the code that I threw it off | Do not add any notes from yourself | Variays: let, const. Functional parts of the code: if, else, for, and so on, you do not need to comment | public/private/protected variables if they are inside the constructor, then they do not need to be commented): 2 | 3 | CODE 4 | 5 | Write your answer in the format: 6 | |class/constructor/function/variable/type/interface Name| 7 | Comment: ... 8 | 9 | Example: 10 | From code: 11 | class Prototype { 12 | public primitive: any; 13 | public component: object; 14 | public circularReference: ComponentWithBackReference; 15 | public dots: [ 16 | Dot, 17 | Dot, 18 | Dot?, 19 | Dot?, 20 | Dot?, 21 | Dot?, 22 | Dot?, 23 | Dot?, 24 | Dot?, 25 | Dot?, 26 | ]; 27 | 28 | constructor(){ 29 | 30 | } 31 | 32 | public clone(): this { 33 | const clone = Object.create(this); 34 | 35 | clone.component = Object.create(this.component); 36 | clone.circularReference = { 37 | ...this.circularReference, 38 | prototype: { ...this }, 39 | }; 40 | 41 | return clone; 42 | } 43 | } 44 | 45 | interface Shape { 46 | getArea: () => number; 47 | } 48 | 49 | class Rectangle implements Shape { 50 | public constructor(protected readonly width: number, protected readonly height: number) { } 51 | 52 | public getArea(): number { 53 | return this.widththis.height; 54 | } 55 | 56 | public toString(): string { 57 | return `Rectangle[width=${this.width}, height=${this.height}]`; 58 | } 59 | } 60 | 61 | Created comments: 62 | |class Prototype| 63 | Comment: 64 | This class represents a prototype object that can be cloned. 65 | @class 66 | 67 | 68 | |public primitive| 69 | Comment: 70 | A public property that can hold any primitive value. 71 | @type {any} 72 | 73 | 74 | |public component| 75 | Comment: 76 | A public property that holds an object. 77 | @type {object} 78 | 79 | 80 | |public circularReference| 81 | Comment: 82 | A public property that holds a reference to another object of type ComponentWithBackReference. 83 | @type {ComponentWithBackReference} 84 | 85 | 86 | |public dots| 87 | Comment: 88 | A public property that holds the dots of the prototype. 89 | @type {[Dot, Dot, Dot?, Dot?, Dot?, Dot?, Dot?, Dot?, Dot?, Dot?]} 90 | 91 | 92 | |constructor| 93 | Comment: 94 | Creates a new Prototype instance. 95 | @constructor 96 | 97 | 98 | |public clone| 99 | Comment: 100 | Returns the clone of the object. 101 | @returns {this} 102 | 103 | 104 | |interface Shape| 105 | Comment: 106 | This interface represents a shape. 107 | @interface 108 | 109 | 110 | |class Rectangle| 111 | Comment: 112 | This class represents a rectangle shape and implements the Shape interface. 113 | @implements {Shape} 114 | @class 115 | 116 | 117 | |constructor| 118 | Comment: 119 | Creates a new Rectangle instance with the given width and height. 120 | @constructor 121 | @param {number} width - The width of the rectangle. 122 | @param {number} height - The height of the rectangle. 123 | 124 | 125 | |public getArea| 126 | Comment: 127 | Calculates and returns the area of the rectangle. 128 | @returns {number} - The area of the rectangle. 129 | 130 | 131 | |public toString| 132 | Comment: 133 | Returns a string representation of the rectangle. 134 | @returns {string} - The string representation of the rectangle. -------------------------------------------------------------------------------- /modules/autodoc.py: -------------------------------------------------------------------------------- 1 | import os 2 | from time import sleep 3 | 4 | from colorama import init as colorama_init 5 | 6 | from revChatGPT.V1 import Chatbot 7 | from modules.prompt import Prompt 8 | from modules.divider import Divider 9 | from modules.settings import * 10 | from modules.result import Result 11 | 12 | from rich.console import Console 13 | 14 | console = Console() 15 | 16 | colorama_init() 17 | 18 | 19 | class AutoDoc: 20 | def __init__(self, config: str, code: str, language: str): 21 | self.__config = config 22 | 23 | self.__code = code 24 | if not isinstance(self.__code, str): 25 | exit(f"[{RED}Error{RESET}] Code is must be a string") 26 | elif len(self.__code) == 0: 27 | exit(f"[{RED}Error{RESET}] Code is empty") 28 | 29 | self.__language = language 30 | if not self.__language in Settings.supported_languages: 31 | exit(f"[{RED}Error{RESET}] .{self.__language} language not supported") 32 | 33 | def __ask(self, code) -> str: 34 | response = None 35 | conversation = None 36 | try: 37 | for data in self.__chatbot.ask( 38 | Prompt(self.__language, code).create(), 39 | conversation_id=self.__conversation, 40 | parent_id=None, 41 | ): 42 | response = data["message"] 43 | conversation = data["conversation_id"] 44 | if not self.__conversation: 45 | self.__conversation = conversation 46 | return response 47 | except: 48 | raise ValueError("ChatGPT error") 49 | 50 | def start(self): 51 | with console.status("[bold green] Please wait...") as status: 52 | print(f"[{BLUE}1{RESET}] {BOLD}Dividing text{RESET}") 53 | self.__divided = Divider(self.__code, self.__language).divide() 54 | 55 | if len(self.__divided) == 0: 56 | exit( 57 | f"[{RED}END{RESET}] {BOLD}There is nothing to comment in the code{RESET}" 58 | ) 59 | 60 | print( 61 | f"[{BLUE}2{RESET}] {BOLD}Text is divided into {len(self.__divided)} parts{RESET}" 62 | ) 63 | 64 | print(f"[{BLUE}3{RESET}] {BOLD}Connecting to ChatGPT{RESET}") 65 | if self.__config["session_token"]: 66 | self.__chatbot = Chatbot( 67 | config={ 68 | "session_token": self.__config["session_token"], 69 | } 70 | ) 71 | elif self.__config["email"] and self.__config["password"]: 72 | self.__chatbot = Chatbot( 73 | config={ 74 | "email": self.__config["email"], 75 | "password": self.__config["password"], 76 | } 77 | ) 78 | else: 79 | raise ValueError("No login details") 80 | 81 | self.__conversation = None 82 | 83 | self.__count = 1 84 | self.__comments = [] 85 | for entity in self.__divided: 86 | print( 87 | f"[{BLUE}4.{self.__count}{RESET}] {BOLD}Chatbot generating comments ({self.__count}/{len(self.__divided)}){RESET}" 88 | ) 89 | response = self.__ask(entity) 90 | self.__comments.append(response) 91 | sleep(1) 92 | self.__count += 1 93 | 94 | print(f"[{BLUE}5{RESET}] {BOLD}Merge file and comments{RESET}") 95 | self.__commented_code = self.__code 96 | for comment in self.__comments: 97 | self.__commented_code = Result( 98 | self.__commented_code, comment, self.__language 99 | ).get() 100 | 101 | print(f"[{BLUE}6{RESET}] {BOLD}Ready. Thanks for using!{RESET}"), 102 | return self.__commented_code 103 | -------------------------------------------------------------------------------- /modules/divider.py: -------------------------------------------------------------------------------- 1 | import re 2 | from modules.settings import Settings 3 | 4 | 5 | class Divider: 6 | def __init__(self, text: str, language: str): 7 | self.__text = text 8 | self.__language = language 9 | self.__splitted_content = [] 10 | 11 | def divide( 12 | self, 13 | ) -> list[str]: 14 | starts = re.finditer(Settings.divide_start[self.__language], self.__text) 15 | ends = re.finditer(Settings.divide_end[self.__language], self.__text) 16 | 17 | while True: 18 | start = None 19 | end = None 20 | try: 21 | start = next(starts).start() 22 | end = next(ends).start() 23 | if end < start: 24 | end = next(ends).start() 25 | self.__splitted_content.append(self.__text[start:end]) 26 | except: 27 | if start != None: 28 | self.__splitted_content.append(self.__text[start:]) 29 | break 30 | 31 | return self.__splitted_content 32 | -------------------------------------------------------------------------------- /modules/file.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | class File: 5 | def __init__(self, path: str): 6 | self.__path = os.path.normpath(os.path.abspath(path)) 7 | self.__dirname = os.path.dirname(self.__path) 8 | self.__basename = os.path.basename(self.__path).split(".")[0] 9 | self.__language = os.path.basename(self.__path).split(".")[1] 10 | 11 | def content(self) -> str: 12 | self.__text = open(self.__path, "r").read() 13 | return self.__text 14 | 15 | def language(self) -> str: 16 | return self.__language 17 | 18 | def create_commented_file(self, content: str) -> None: 19 | path = os.path.join( 20 | self.__dirname, 21 | self.__basename + f"_commented.{self.__language}", 22 | ) 23 | open(path, "w").write(content) 24 | -------------------------------------------------------------------------------- /modules/prompt.py: -------------------------------------------------------------------------------- 1 | class Prompt: 2 | def __init__(self, language: str, text: str): 3 | self.__text = text 4 | self.__language = language 5 | 6 | def create(self) -> str: 7 | return ( 8 | open(f"./models/{self.__language}.txt", "r") 9 | .read() 10 | .replace("CODE", self.__text) 11 | ) 12 | -------------------------------------------------------------------------------- /modules/result.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | class Result: 5 | def __init__(self, code: str, text_comment: str, language: str): 6 | self.__code = code 7 | self.__text_comment = text_comment 8 | self.__language = language 9 | 10 | def get(self) -> str: 11 | if self.__language == "py": 12 | return self.__py() 13 | elif self.__language == "ts" or self.__language == "js": 14 | return self.__ts() 15 | 16 | def __py(self) -> str: 17 | regexpes = re.finditer(r"\|(.+?)\|", self.__text_comment) 18 | indexes = [] 19 | 20 | while True: 21 | try: 22 | match = next(regexpes) 23 | start = match.start() 24 | end = match.end() 25 | indexes.append((start, end)) 26 | except: 27 | break 28 | 29 | names = [] 30 | comments = [] 31 | for i in range(len(indexes)): 32 | names.append(self.__text_comment[indexes[i][0] + 1 : indexes[i][1] - 1]) 33 | if i < len(indexes) - 1: 34 | comments.append( 35 | self.__text_comment[indexes[i][1] + 1 : indexes[i + 1][0]].replace( 36 | "Description: ", "" 37 | ) 38 | ) 39 | else: 40 | comments.append( 41 | self.__text_comment[indexes[i][1] :].replace("Description: ", "") 42 | ) 43 | 44 | for i in range(len(names)): 45 | index = self.__code.index(names[i]) 46 | split_start = self.__code[index:] 47 | doubledot = index + split_start.index(":\n") + 1 48 | tabs = re.search(r":\n(.*?)[a-zA-Z]", split_start).span() 49 | tabs_count = tabs[1] - tabs[0] 50 | tab = (tabs_count - 3) * " " 51 | replace_text = self.__code[index:doubledot] 52 | new_replace_text = comments[i].replace("\n", f"\n{tab}") 53 | self.__code = self.__code.replace( 54 | replace_text, 55 | f'{replace_text}\n{tab}"""\n{tab}{new_replace_text}\n{tab}"""', 56 | ) 57 | return self.__code 58 | 59 | def __ts(self) -> str: 60 | regexpes = re.finditer(r"\|(.+?)\|\n", self.__text_comment) 61 | indexes = [] 62 | 63 | while True: 64 | try: 65 | match = next(regexpes) 66 | start = match.start() 67 | end = match.end() - 1 68 | indexes.append((start, end)) 69 | except: 70 | break 71 | 72 | names: list[str] = [] 73 | comments: list[str] = [] 74 | for i in range(len(indexes)): 75 | names.append(self.__text_comment[indexes[i][0] + 1 : indexes[i][1] - 1]) 76 | if i < len(indexes) - 1: 77 | comments.append( 78 | re.sub( 79 | r"Comment:\n|Comment: \n", 80 | "", 81 | self.__text_comment[indexes[i][1] + 1 : indexes[i + 1][0]], 82 | ) 83 | ) 84 | else: 85 | comments.append( 86 | self.__text_comment[indexes[i][1] :].replace("Comment:\n", "") 87 | ) 88 | 89 | for i in range(len(names)): 90 | comments[i] = " * " + comments[i].replace("\n", "\n * ") 91 | comments[i] = re.sub(r"\n\s\*\s\n\s\*\s", "", comments[i]) 92 | index = self.__code.find(names[i]) 93 | 94 | for j in range(index, 0, -1): 95 | if self.__code[j] == "\n": 96 | from_new_string = self.__code[j + 1 :] 97 | tabs_end = re.search(r"[a-zA-Z]", from_new_string).start() 98 | tabs = from_new_string[:tabs_end] 99 | comments[i] = re.sub(r"\n", f"\n{tabs}", comments[i]) 100 | splitted_code = list(self.__code) 101 | splitted_code[j] = f"\n{tabs}/**\n{tabs}{comments[i]}\n{tabs} */\n" 102 | self.__code = "".join(splitted_code) 103 | break 104 | else: 105 | self.__code = f"\n/**\n{comments[i]}\n */\n" + self.__code 106 | return self.__code 107 | -------------------------------------------------------------------------------- /modules/settings.py: -------------------------------------------------------------------------------- 1 | from colorama import Fore, Style 2 | 3 | RESET = Style.RESET_ALL 4 | GREEN = Fore.GREEN 5 | BLUE = Fore.BLUE 6 | RED = Fore.RED 7 | YELLOW = Fore.YELLOW 8 | BOLD = "\033[1m" 9 | 10 | 11 | class Settings: 12 | supported_languages = ["py", "ts", "js"] 13 | divide_start = { 14 | "py": r"(? 0: 66 | self.__language = "example" 67 | 68 | def __ask(self, code) -> str: 69 | """ 70 | Sends a prompt to the chatbot API and returns the response. 71 | 72 | Arguments: 73 | 74 | code (str): A string containing the code prompt to send to the chatbot API. 75 | 76 | Returns: 77 | 78 | str: A string containing the response from the chatbot API. 79 | 80 | 81 | """ 82 | response = None 83 | conversation = None 84 | try: 85 | for data in self.__chatbot.ask( 86 | Prompt(self.__language, code).create(), 87 | conversation_id=self.__conversation, 88 | parent_id=None, 89 | ): 90 | response = data["message"] 91 | conversation = data["conversation_id"] 92 | if not self.__conversation: 93 | self.__conversation = conversation 94 | return response 95 | except: 96 | raise ValueError("ChatGPT error") 97 | 98 | def start(self): 99 | """ 100 | 101 | Generates comments for the code using the chatbot API. 102 | 103 | Arguments: None 104 | 105 | Returns: 106 | 107 | str: A string containing the original code with comments generated by the chatbot API. 108 | """ 109 | with console.status("[bold green] Please wait...") as status: 110 | print(f"[{BLUE}1{RESET}] {BOLD}Dividing text{RESET}") 111 | self.__divided = Divider(self.__code, self.__language).divide() 112 | print( 113 | f"[{BLUE}2{RESET}] {BOLD}Text is divided into {len(self.__divided)} parts{RESET}" 114 | ) 115 | 116 | print(f"[{BLUE}3{RESET}] {BOLD}Connecting to ChatGPT{RESET}") 117 | self.__chatbot = Chatbot({"session_token": self.__token}) 118 | self.__conversation = None 119 | 120 | self.__count = 1 121 | self.__comments = [] 122 | for entity in self.__divided: 123 | print( 124 | f"[{BLUE}4.{self.__count}{RESET}] {BOLD}Chatbot generating comments ({self.__count}/{len(self.__divided)}){RESET}" 125 | ) 126 | response = self.__ask(entity) 127 | self.__comments.append(response) 128 | sleep(1) 129 | self.__count += 1 130 | 131 | print(f"[{BLUE}5{RESET}] {BOLD}Merge file and comments{RESET}") 132 | self.__commented_code = self.__code 133 | for comment in self.__comments: 134 | self.__commented_code = Result( 135 | self.__commented_code, comment, self.__language 136 | ).get() 137 | 138 | print(f"[{BLUE}6{RESET}] {BOLD}Ready. Thanks for using!{RESET}"), 139 | return self.__commented_code 140 | -------------------------------------------------------------------------------- /results/python/divider_commented.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | class Divider: 5 | """ 6 | A class that takes a text string and a language string, and splits the text into sections based on language-specific separators. 7 | 8 | Arguments: 9 | 10 | text (str): A string of text to be split 11 | language (str): A string representing the language of the text (e.g. "py" for Python) 12 | 13 | 14 | """ 15 | def __init__(self, text: str, language: str): 16 | """ 17 | Initializes a Divider instance with the given text and language. 18 | 19 | Arguments: 20 | 21 | text (str): A string of text to be split 22 | language (str): A string representing the language of the text (e.g. "py" for Python) 23 | 24 | 25 | """ 26 | self.__text = text 27 | self.__language = language 28 | self.__splitted_content = [] 29 | 30 | def divide( 31 | self, 32 | ) -> list[str]: 33 | """ 34 | Splits the text into sections based on language-specific separators. 35 | 36 | Arguments: None 37 | 38 | Returns: 39 | 40 | list[str]: A list of strings representing the sections of the text. 41 | 42 | 43 | """ 44 | if self.__language == "py": 45 | return self.__py() 46 | 47 | def __py(self) -> list[str]: 48 | """ 49 | 50 | A method that splits Python code into sections based on class and function definitions. 51 | 52 | Arguments: None 53 | 54 | Returns: 55 | 56 | list[str]: A list of strings representing the sections of the Python code. 57 | """ 58 | starts = re.finditer(r"(? str: 32 | """ 33 | Retrieves the content of the file. 34 | 35 | Arguments: None 36 | 37 | Returns: 38 | 39 | str: A string representing the content of the file. 40 | 41 | 42 | """ 43 | self.__text = open(self.__path, "r").read() 44 | return self.__text 45 | 46 | def language(self) -> str: 47 | """ 48 | Retrieves the language of the file. 49 | 50 | Arguments: None 51 | 52 | Returns: 53 | 54 | str: A string representing the language of the file. 55 | 56 | 57 | """ 58 | return self.__language 59 | 60 | def create_commented_file(self, content: str) -> None: 61 | """ 62 | 63 | Creates a new file with the commented content. 64 | 65 | Arguments: 66 | 67 | content (str): A string representing the commented content to be written into the new file. 68 | 69 | Returns: None 70 | """ 71 | path = os.path.join( 72 | self.__dirname, 73 | self.__basename + f"_commented.{self.__language}", 74 | ) 75 | open(path, "w").write(content) 76 | -------------------------------------------------------------------------------- /results/python/prompt_commented.py: -------------------------------------------------------------------------------- 1 | class Prompt: 2 | """ 3 | A class for generating prompts for coding exercises in a specified language. 4 | 5 | Arguments: 6 | 7 | language (str): A string representing the language of the prompt (e.g. "python") 8 | text (str): A string representing the code to be used in the prompt. 9 | example_text (str): Optional. A string representing an example usage of the code in the prompt. 10 | 11 | 12 | """ 13 | def __init__(self, language: str, text: str, example_text: str = ""): 14 | """ 15 | Initializes a Prompt instance with the given language, code, and example text. 16 | 17 | Arguments: 18 | 19 | language (str): A string representing the language of the prompt (e.g. "python") 20 | text (str): A string representing the code to be used in the prompt. 21 | example_text (str): Optional. A string representing an example usage of the code in the prompt. 22 | 23 | 24 | """ 25 | self.__text = text 26 | self.__language = language 27 | self.__example_text = example_text 28 | 29 | def create(self) -> str: 30 | """ 31 | 32 | Generates a prompt using the given language, code, and example text. 33 | 34 | Arguments: None 35 | 36 | Returns: 37 | 38 | str: A string representing the prompt, with placeholders for the code and example text filled in. 39 | """ 40 | if self.__example_text: 41 | return ( 42 | open(f"./models/{self.__language}.txt", "r") 43 | .read() 44 | .replace("CODE", self.__text) 45 | .replace("EXAMPLE", self.__example_text) 46 | ) 47 | else: 48 | return ( 49 | open(f"./models/{self.__language}.txt", "r") 50 | .read() 51 | .replace("CODE", self.__text) 52 | ) 53 | -------------------------------------------------------------------------------- /results/python/result_commented.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | class Result: 5 | """ 6 | This class represents a code result with its associated text comment and language. 7 | 8 | Arguments: 9 | 10 | code (str): A string of code. 11 | text_comment (str): A string of text comment associated with the code. 12 | language (str): A string representing the language of the code (e.g. "py" for Python). 13 | 14 | 15 | """ 16 | def __init__(self, code: str, text_comment: str, language: str): 17 | """ 18 | Initializes a Result instance with the given code, text comment, and language. 19 | 20 | Arguments: 21 | 22 | code (str): A string of code. 23 | text_comment (str): A string of text comment associated with the code. 24 | language (str): A string representing the language of the code (e.g. "py" for Python). 25 | 26 | 27 | """ 28 | self.__code = code 29 | self.__text_comment = text_comment 30 | self.__language = language 31 | 32 | def get(self) -> str: 33 | """ 34 | Returns a modified version of the code with the text comments included as docstrings for the appropriate class and function definitions in the code. 35 | 36 | Arguments: None 37 | 38 | Returns: 39 | 40 | str: A string representing the modified version of the code. 41 | 42 | 43 | """ 44 | if self.__language == "py": 45 | return self.__py() 46 | 47 | def __py(self) -> str: 48 | """ 49 | A private method that splits Python code into sections based on the " 50 | """ 51 | regexpes = re.finditer(r"\|(.+?)\|", self.__text_comment) 52 | indexes = [] 53 | 54 | while True: 55 | try: 56 | match = next(regexpes) 57 | start = match.start() 58 | end = match.end() 59 | indexes.append((start, end)) 60 | except: 61 | break 62 | 63 | names = [] 64 | comments = [] 65 | for i in range(len(indexes)): 66 | """ 67 | " and "..." separators in the text comment associated with the code. 68 | 69 | Arguments: None 70 | 71 | Returns: 72 | 73 | str: A string representing the modified version of the Python code. 74 | """ 75 | names.append(self.__text_comment[indexes[i][0] + 1 : indexes[i][1] - 1]) 76 | if i < len(indexes) - 1: 77 | comments.append( 78 | self.__text_comment[indexes[i][1] + 1 : indexes[i + 1][0]].replace( 79 | "Description: ", "" 80 | ) 81 | ) 82 | else: 83 | comments.append( 84 | self.__text_comment[indexes[i][1] :].replace("Description: ", "") 85 | ) 86 | 87 | for i in range(len(names)): 88 | index = self.__code.index(names[i]) 89 | split_start = self.__code[index:] 90 | doubledot = index + split_start.index(":\n") + 1 91 | tabs = re.search(r":\n(.*?)[a-zA-Z]", split_start).span() 92 | tabs_count = tabs[1] - tabs[0] 93 | tab = (tabs_count - 3) * " " 94 | replace_text = self.__code[index:doubledot] 95 | new_replace_text = comments[i].replace("\n", f"\n{tab}") 96 | self.__code = self.__code.replace( 97 | replace_text, 98 | f'{replace_text}\n{tab}"""\n{tab}{new_replace_text}\n{tab}"""', 99 | ) 100 | return self.__code 101 | -------------------------------------------------------------------------------- /results/python/settings_commented.py: -------------------------------------------------------------------------------- 1 | from colorama import Fore, Style 2 | 3 | RESET = Style.RESET_ALL 4 | GREEN = Fore.GREEN 5 | BLUE = Fore.BLUE 6 | RED = Fore.RED 7 | YELLOW = Fore.YELLOW 8 | BOLD = "\033[1m" 9 | 10 | 11 | class Settings: 12 | """ 13 | 14 | This class stores the settings for a program. 15 | 16 | Variables: 17 | - supported_languages (list): A list of supported languages. 18 | 19 | Example Usage: 20 | ```python 21 | settings = Settings() 22 | print(settings.supported_languages) # Output: ["py"] 23 | ``` 24 | 25 | Note: There are no functions defined in this class. 26 | """ 27 | supported_languages = ["py"] 28 | -------------------------------------------------------------------------------- /results/ts/example_2_commented.ts: -------------------------------------------------------------------------------- 1 | import * as http from "http"; 2 | 3 | 4 | /** 5 | * 6 | * This type represents the HTTP methods that can be used for making requests. 7 | * @typedef {'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATСH'} protocols 8 | */ 9 | type protocols = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATСH' 10 | 11 | 12 | 13 | /** 14 | * This class represents a server and creates an instance of http.Server. 15 | * @class 16 | * 17 | */ 18 | export default class Server { 19 | /** 20 | * A private property that holds an instance of http.Server. 21 | * @type {http.Server} 22 | * 23 | */ 24 | private server: http.Server; 25 | /** 26 | * A private property that holds the port number. 27 | * @type {string|number} 28 | * 29 | */ 30 | private port!: string | number; 31 | /** 32 | * Creates a new Server instance with the given serverResponse and serverError functions. 33 | * @constructor 34 | * @param {function} serverResponse - The function that will handle server response. 35 | * @param {function} serverError - The function that will handle server error. 36 | * 37 | */ 38 | constructor(serverResponse: (data: string, req: http.IncomingMessage, res: http.ServerResponse) => void, serverError: (req: http.IncomingMessage, res: http.ServerResponse) => void) { 39 | this.server = http 40 | .createServer((req: http.IncomingMessage, res: http.ServerResponse) => { 41 | if (req.method == "GET") { 42 | if (req.url) { 43 | let body = ''; 44 | req.on('data', chunk => { 45 | body += chunk.toString(); 46 | }); 47 | req.on('end', () => { 48 | res.setHeader('Content-Type', 'application/json'); 49 | try { 50 | serverResponse(body, req, res); 51 | } catch (error) { 52 | serverError(req, res) 53 | } 54 | }); 55 | } 56 | } 57 | }) 58 | } 59 | 60 | /** 61 | * 62 | * Starts the server on the specified port. 63 | * @param {string|number} port - The port number on which the server will listen. 64 | * @returns {void} 65 | */ 66 | public listen(port: string | number,) { 67 | this.port = port; 68 | this.server.listen(this.port); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /results/ts/example_commented.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This class represents a Pizza and has a constructor that initializes its name and toppings. 4 | * @class 5 | * 6 | */ 7 | class Pizza { 8 | constructor( 9 | /** 10 | * A public property that holds the name of the pizza. 11 | * @type {string} 12 | * 13 | */ 14 | public name: string, 15 | /** 16 | * 17 | * A public property that holds an array of strings representing the pizza toppings. 18 | * @type {string[]} 19 | */ 20 | public toppings: string[] 21 | ) { }; 22 | } 23 | /** 24 | * This class provides a static method to create a Pizza object. 25 | * @class 26 | * 27 | */ 28 | class PizzaMaker { 29 | /** 30 | * Creates a new Pizza object with the given name and toppings. 31 | * @static 32 | * @param {Pizza} event - The Pizza object to create. 33 | * @returns {Pizza} - The newly created Pizza object. 34 | * 35 | */ 36 | static create(event: Pizza) { 37 | return new Pizza( 38 | event.name, 39 | event.toppings 40 | ); 41 | } 42 | } 43 | /** 44 | * 45 | * Creates a new Pizza object using the PizzaMaker class. 46 | * @const 47 | * @type {Pizza} 48 | */ 49 | const pizza = PizzaMaker.create({ 50 | name: 'Inferno', 51 | toppings: ['cheese', 'peppers'] 52 | }); --------------------------------------------------------------------------------