Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions commit/api/api_explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def get_apis_for_project(project_branch: str):
for doc in documentation:
if doc.get("function_name") == api.get("name") and doc.get("path") == api.get("api_path"):
api["documentation"] = doc.get("documentation")
api["last_updated"] = doc.get("last_updated")
break

app_name, organization, app_logo = frappe.db.get_value("Commit Project", branch_doc.project, ["app_name", "org", "image"])
Expand Down
106 changes: 92 additions & 14 deletions commit/api/generate_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,24 +61,22 @@ def generate_docs_for_chunk(api_chunk):
"content": (
"You are an expert documentation generator. Create detailed and comprehensive documentation "
"for the code provided below in Markdown format. Each function should have the following sections:\n\n"
"- **(api[Function Name])**\n"
"- **Description**: Detailed description of what the function does and what it is used for \n"
"- **Parameters**: List of parameters with their types, descriptions, and indicate which are mandatory or optional\n"
"- **Return Type**: Type and description of the return value\n"
"- **Examples**: Code examples demonstrating how to use the function (enclosed in triple backticks ``````).\n\n"
"- # [Function Name] (as heading 1)\n"
"- ## Description: Detailed description of what the function does and what it is used for \n"
"- ## Parameters: List of parameters with their types, descriptions, and indicate which are mandatory or optional\n"
"- ## Return Type: Type and description of the return value\n"
"- ## Examples: Code examples demonstrating how to use the function (enclosed using <pre> and <code> Tags`).\n\n"
"The response should be a valid JSON list of objects formatted as follows: "
"{function_name: <function_name>, path: <path>, documentation: <documentation in Markdown>}.\n"
"{function_name: <function_name>, path: <path>, last_updated:<last_updated>, documentation: <documentation in Markdown>}.\n"
"Ensure the response is in valid JSON format only, enclosed in triple backticks, and does not include `---`."
)
}
]

last_updated = frappe.utils.now()
for api in api_chunk:
user_message = f"function name: {api['function_name']}, path: {api['path']}, code:\n{api['code']}"
user_message = f"function name: {api['function_name']}, path: {api['path']}, last_updated:{last_updated} ,code:\n{api['code']}"
messages.append({"role": "user", "content": user_message})

# print("Raw Response:\n", response_text) # Log raw response for debugging

response_text = open_ai_call(messages)

cleaned_response = clean_response(response_text)
Expand All @@ -103,7 +101,7 @@ def generate_docs_for_chunk(api_chunk):
return json.loads(cleaned_response, strict=False)
except json.JSONDecodeError as e:
print("Second JSON Decode Error:", e)
return []
return generate_docs_for_chunk(api_chunk)

# return cleaned_response

Expand All @@ -120,13 +118,13 @@ def generate_documentation_for_api_snippet(api_path:str,code_snippet:str):
"- ## Return Type\n Specify the type and description of the return value.\n"
"- ## Examples\n Provide code examples demonstrating how to use the function, enclosed in triple backticks (``````).\n\n"
"The response should be a valid JSON formatted as follows: "
"{function_name: <function_name>, path: <path>, documentation: <documentation in Markdown>}.\n"
"{function_name: <function_name>, path: <path>, last_updated:<last_updated>, documentation: <documentation in Markdown>}.\n"
"Ensure the response is in valid JSON format only, and does not include `---`."
)
}
]

user_message = f"api path: {api_path}, code:\n{code_snippet}"
user_message = f"api path: {api_path}, last_updated:{frappe.utils.now()}, code:\n{code_snippet}"
if not code_snippet:
return []
messages.append({"role": "user", "content": user_message})
Expand Down Expand Up @@ -161,4 +159,84 @@ def generate_documentation_for_api_snippet(api_path:str,code_snippet:str):
def get_documentation_for_api(project_branch: str, file_path: str,block_start: int, block_end: int,endpoint:str,viewer_type:str = 'app'):
code_snippet = get_file_content_from_path(project_branch, file_path,block_start, block_end,viewer_type)
api_path = endpoint
return generate_documentation_for_api_snippet(api_path, code_snippet)
return generate_documentation_for_api_snippet(api_path, code_snippet)

@frappe.whitelist()
def save_documentation(project_branch:str,endpoint:str,documentation:str,viewer_type:str = 'app'):
# Save the documentation to the project branch
# 1. Check for viewer_type app or project
# 2. If viewer_type is app, then check the document is already present in Commit Branch Documentation doctype
# 3. If document present then loop over documentation check if the function_name and path matches then update the documentation else create a new dict and append to the documentation
# 4. If document not present then create a new document and append the documentation
# 5. If viewer_type is project then check the document is already present in Commit Project Branch doctype
# 6. If document present then loop over documentation check if the function_name and path matches then update the documentation else create a new dict and append to the documentation
# 7. If document not present then create a new document and append the documentation

if viewer_type == "app":
# Check if the document is already present in Commit Branch Documentation doctype
save_documentation_for_site_app(project_branch, endpoint, documentation)
else:
save_documentation_for_project_branch(project_branch, endpoint, documentation)

def save_documentation_for_project_branch(project_branch:str,endpoint:str,documentation:str):

doc = frappe.get_doc("Commit Project Branch", project_branch)
docs = json.loads(doc.documentation) if doc.documentation else {}
apis = docs.get("apis", [])

# apis is list of dict with keys function_name, path, last_updated, documentation
# loop over apis and check if function_name and path matches then update the documentation else create a new dict and append to the documentation
found = False
for api in apis:
if api.get("function_name") == endpoint.split(".")[-1] and api.get("path") == endpoint:
api["documentation"] = documentation
api["last_updated"] = frappe.utils.now()
found = True
break
if not found:
apis.append({
"function_name": endpoint.split(".")[-1],
"path": endpoint,
"last_updated": frappe.utils.now(),
"documentation": documentation
})

doc.documentation = json.dumps({"apis": apis})
doc.save()

def save_documentation_for_site_app(project_branch:str,endpoint:str,documentation:str):

if frappe.db.exists("Commit Branch Documentation",project_branch):
doc = frappe.get_doc("Commit Branch Documentation", project_branch)
docs = json.loads(doc.documentation) if doc.documentation else {}
apis = docs.get("apis", [])

# apis is list of dict with keys function_name, path, last_updated, documentation
# loop over apis and check if function_name and path matches then update the documentation else create a new dict and append to the documentation
found = False
for api in apis:
if api.get("function_name") == endpoint.split(".")[-1] and api.get("path") == endpoint:
api["documentation"] = documentation
api["last_updated"] = frappe.utils.now()
found = True
break
if not found:
apis.append({
"function_name": endpoint.split(".")[-1],
"path": endpoint,
"last_updated": frappe.utils.now(),
"documentation": documentation
})
doc.documentation = json.dumps({"apis": apis})
doc.save()
else:
# Create a new document and append the documentation
doc = frappe.new_doc("Commit Branch Documentation")
doc.app = project_branch
doc.documentation = json.dumps({"apis": [{
"function_name": endpoint.split(".")[-1],
"path": endpoint,
"last_updated": frappe.utils.now(),
"documentation": documentation
}]})
doc.save()
36 changes: 31 additions & 5 deletions commit/commit/code_analysis/apis.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import os
import ast
import frappe
import json

other_decorators = [
'@cache_source',
Expand Down Expand Up @@ -33,7 +35,7 @@ def find_all_occurrences_of_whitelist(path: str, app_name: str):
# if file.endswith('party.py'):
indexes,line_nos,no_of_occurrences = find_indexes_of_whitelist(file_content, no_of_occurrences)
api_count += no_of_occurrences
apis = get_api_details(file, file_content, indexes,line_nos, path)
apis = get_api_details(app_name, file, file_content, indexes,line_nos, path)
api_details.extend(apis)

return api_details
Expand Down Expand Up @@ -109,7 +111,7 @@ def is_in_string_or_comment(file_content, index):

return indexes, line_nos, actual_count

def get_api_details(file, file_content: str, indexes: list,line_nos:list, path: str):
def get_api_details(app_name, file, file_content: str, indexes: list,line_nos:list, path: str):
'''
Get details of the API
'''
Expand All @@ -118,7 +120,7 @@ def get_api_details(file, file_content: str, indexes: list,line_nos:list, path:
whitelist_details = get_whitelist_details(file_content, index)
api_details = get_api_name(file_content, index)
other_decorators = get_other_decorators(file_content, index, api_details.get('def_index'))
apis.append({
obj = {
**api_details,
**whitelist_details,
'other_decorators': other_decorators,
Expand All @@ -127,7 +129,11 @@ def get_api_details(file, file_content: str, indexes: list,line_nos:list, path:
'block_end': find_function_end_lines(file_content,api_details.get('name','')),
'file': file,
'api_path': file.replace(path, '').replace('\\', '/').replace('.py', '').replace('/', '.')[1:] + '.' + api_details.get('name')
})
}
documentation, last_updated = get_documentation_from_branch_documentation(app_name, obj.get('name'), obj.get('api_path'))
obj['documentation'] = documentation
obj['last_updated'] = last_updated
apis.append(obj)

return apis

Expand Down Expand Up @@ -286,4 +292,24 @@ def get_decorators(node):
decorator_name = get_decorator_name(decorator)
if decorator_name is not None:
decorators.append(decorator_name)
return decorators
return decorators

def get_documentation_from_branch_documentation(app_name:str, name: str, api_path: str):
'''
Get documentation from the Commit Branch Documentation
'''
if frappe.db.exists('Commit Branch Documentation',app_name):
branch_documentation = frappe.get_doc('Commit Branch Documentation', app_name)
docs = json.loads(branch_documentation.documentation) if branch_documentation.documentation else {}
apis = docs.get("apis", [])
documentation = ''
last_updated = ''
for api in apis:
if api.get("function_name") == name and api.get("path") == api_path:
documentation = api.get("documentation")
last_updated = api.get("last_updated")
break
return documentation, last_updated
else:
return '', ''

Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) 2024, The Commit Company and contributors
// For license information, please see license.txt

// frappe.ui.form.on("Commit Branch Documentation", {
// refresh(frm) {

// },
// });
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:app",
"creation": "2024-09-29 12:24:08.997955",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"app",
"documentation"
],
"fields": [
{
"fieldname": "app",
"fieldtype": "Data",
"in_list_view": 1,
"label": "App",
"reqd": 1,
"unique": 1
},
{
"fieldname": "documentation",
"fieldtype": "JSON",
"label": "Documentation"
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-09-29 12:29:40.251915",
"modified_by": "Administrator",
"module": "commit",
"name": "Commit Branch Documentation",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2024, The Commit Company and contributors
# For license information, please see license.txt

# import frappe
from frappe.model.document import Document


class CommitBranchDocumentation(Document):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2024, The Commit Company and Contributors
# See license.txt

# import frappe
from frappe.tests.utils import FrappeTestCase


class TestCommitBranchDocumentation(FrappeTestCase):
pass
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,17 @@ export const AsyncSpinnerLoader: React.FC<AsyncSpinnerLoaderProps> = ({

export interface SpinnerLoaderProps {
className?: string;
style?: React.CSSProperties;
}

export const SpinnerLoader = ({ className }: SpinnerLoaderProps) => {
export const SpinnerLoader = ({ className, style }: SpinnerLoaderProps) => {

return (
<div
className={`inline-block h-4 w-4 mr-2 animate-spin rounded-full border-2 border-solid border-current text-gray-200 border-e-transparent align-[-0.125em] text-surface motion-reduce:animate-[spin_1.5s_linear_infinite] dark:text-white ${className}`}
role="status">
role="status"
style={style}
>
</div>
)
}
Loading