Skip to content
Closed
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
90 changes: 43 additions & 47 deletions penify_hook/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,35 @@

class APIClient:
def __init__(self, api_url, api_token: str = None, bearer_token: str = None):
"""Initialize a new instance of the API client.

Args:
api_url (str): The base URL of the API.
api_token (str?): An API token for authentication. Defaults to None.
bearer_token (str?): A Bearer token for authentication. Defaults to None.
"""
self.api_url = api_url
self.AUTH_TOKEN = api_token
self.BEARER_TOKEN = bearer_token

def send_file_for_docstring_generation(self, file_name, content, line_numbers, repo_details = None):
"""Send file content and modified lines to the API and return modified
content.

This function constructs a payload containing the file path, content,
and modified line numbers, and sends it to a specified API endpoint for
processing. It handles the response from the API, returning the modified
content if the request is successful. If the request fails, it logs the
error details and returns the original content.

"""Send file content and modified lines to the API and return modified content.

This function constructs a payload containing the file path, content, and modified line numbers, and sends it to a
specified API endpoint for processing. It handles the response from the API, returning the modified content if the
request is successful. If the request fails, it logs the error details and returns the original content.

Args:
file_name (str): The path to the file being sent.
content (str): The content of the file to be processed.
line_numbers (list): A list of line numbers that have been modified.
repo_details (str?): Additional repository details if applicable. Defaults to None.

repo_details (str??): Additional repository details if applicable. Defaults to None.
Returns:
str: The modified content returned by the API, or the original content if the
request fails.

str: The modified content returned by the API, or the original content if the request fails.

Raises:
Exception: If there is an error in processing the file and no specific error
message is provided.
Exception: If there is an error in processing the file and no specific error message is provided.
"""
payload = {
'file_path': file_name,
Expand All @@ -54,22 +56,20 @@ def send_file_for_docstring_generation(self, file_name, content, line_numbers, r

def generate_commit_summary(self, git_diff, instruction: str = "", repo_details = None, jira_context: dict = None):
"""Generate a commit summary by sending a POST request to the API endpoint.

This function constructs a payload containing the git diff and any
additional instructions provided. It then sends this payload to a
specified API endpoint to generate a summary of the commit. If the
request is successful, it returns the response from the API; otherwise,
it returns None.


This function constructs a payload containing the git diff and any additional instructions provided. It then sends this
payload to a specified API endpoint to generate a summary of the commit. If the request is successful, it returns the
response from the API; otherwise, it returns None.

Args:
git_diff (str): The git diff of the commit.
instruction (str??): Additional instruction for the commit. Defaults to "".
repo_details (dict??): Details of the git repository. Defaults to None.
jira_context (dict??): JIRA issue details to enhance the commit summary. Defaults to None.

instruction (str?): Additional instruction for the commit. Defaults to "".
repo_details (dict?): Details of the git repository. Defaults to None.
jira_context (dict?): JIRA issue details to enhance the commit summary. Defaults to None.
Returns:
dict: The response from the API if the request is successful, None otherwise.

Raises:
Exception: If there is an error during the API request.
"""
Expand Down Expand Up @@ -100,18 +100,16 @@ def generate_commit_summary(self, git_diff, instruction: str = "", repo_details
return None

def get_supported_file_types(self) -> list[str]:
"""Retrieve the supported file types from the API.

This function sends a request to the API endpoint
`/v1/file/supported_languages` to obtain a list of supported file types.
If the API call is successful (status code 200), it parses the JSON
response and returns the list of supported file types. If the API call
fails, it returns a default list of common file types.

"""Retrieve the supported file types from the API.

This function sends a request to the API endpoint `/v1/file/supported_languages` to obtain a list of supported file
types. If the API call is successful (status code 200), it parses the JSON response and returns the list of supported
file types. If the API call fails, it returns a default list of common file types.

Returns:
list[str]: A list of supported file types, either from the API or a default set.
"""

url = self.api_url+"/v1/file/supported_languages"
response = requests.get(url)
if response.status_code == 200:
Expand All @@ -121,18 +119,17 @@ def get_supported_file_types(self) -> list[str]:
return ["py", "js", "ts", "java", "kt", "cs", "c"]

def generate_commit_summary_with_llm(self, diff, message, generate_description: bool, repo_details, llm_client : LLMClient, jira_context=None):
"""Generates a commit summary using a local LLM client. If an error occurs
during the generation process,
"""Generates a commit summary using a local LLM client. If an error occurs during the generation process,
it falls back to using the API.

Args:
diff (str): The Git diff of changes.
message (str): User-provided commit message or instructions.
generate_description (bool): Flag indicating whether to generate a description for the commit.
repo_details (dict): Details about the repository.
llm_client (LLMClient): An instance of LLMClient used to generate the summary.
jira_context (JIRAContext?): Optional JIRA issue context to enhance the summary.

jira_context (JIRAContext??): Optional JIRA issue context to enhance the summary.
Returns:
dict: A dictionary containing the title and description for the commit.
"""
Expand All @@ -144,17 +141,16 @@ def generate_commit_summary_with_llm(self, diff, message, generate_description:
return self.generate_commit_summary(diff, message, repo_details, jira_context)

def get_api_key(self):
"""Fetch an API key from a specified URL.

This function sends a GET request to retrieve an API token using a
Bearer token in the headers. It handles the response and returns the API
key if the request is successful, or `None` otherwise.

"""Fetch an API key from a specified URL.

This function sends a GET request to retrieve an API token using a Bearer token in the headers. It handles the response
and returns the API key if the request is successful, or `None` otherwise.

Returns:
str: The API key if the request is successful, `None` otherwise.
"""


url = self.api_url+"/v1/apiToken/get"
response = requests.get(url, headers={"Authorization": f"Bearer {self.BEARER_TOKEN}"}, timeout=60*10)
if response.status_code == 200:
Expand Down
9 changes: 9 additions & 0 deletions penify_hook/base_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
class BaseAnalyzer:

def __init__(self, folder_path: str, api_client: APIClient):
"""Manage a Git repository and interact with an API client.

Initializes the repository manager with a folder path and an API client. It sets up the repository details, relative
file path, and supported file types.

Args:
folder_path (str): The path to the local Git repository folder.
api_client (APIClient): An instance of the APIClient for API interactions.
"""
self.folder_path = folder_path
self.repo_path = recursive_search_git_folder(folder_path)
self.repo = None
Expand Down
70 changes: 36 additions & 34 deletions penify_hook/commit_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,38 @@

class CommitDocGenHook(BaseAnalyzer):
def __init__(self, repo_path: str, api_client: APIClient, llm_client=None, jira_client=None):
"""Initialize a new instance of the class.

This method initializes a new instance with the provided repository path, API client, and optionally LLM and JIRA
clients.

Args:
repo_path (str): The path to the repository.
api_client (APIClient): The API client used for communication.
llm_client (LLMClient?): An optional LLM client for additional functionalities. Defaults to None.
jira_client (JiraClient?): An optional JIRA client for integration with JIRA. Defaults to None.
"""
super().__init__(repo_path, api_client)

self.llm_client = llm_client # Add LLM client as an optional parameter
self.jira_client: JiraClient = jira_client # Add JIRA client as an optional parameter

def get_summary(self, instruction: str, generate_description: bool) -> dict:
"""Generate a summary for the commit based on the staged changes.

This function retrieves the differences of the staged changes in the
repository and generates a commit summary using the provided
instruction. If there are no changes staged for commit, an exception is
raised. If a JIRA client is connected, it will attempt to extract issue
keys from the current branch and use them to fetch context. The summary
can be generated either with a Language Model (LLM) client or through
the API client.


This function retrieves the differences of the staged changes in the repository and generates a commit summary using the
provided instruction. If there are no changes staged for commit, an exception is raised. If a JIRA client is connected,
it will attempt to extract issue keys from the current branch and use them to fetch context. The summary can be
generated either with a Language Model (LLM) client or through the API client.

Args:
instruction (str): A string containing instructions for generating the commit summary.
generate_description (bool): Whether to include detailed descriptions in the summary.

Returns:
dict: The generated commit summary based on the staged changes, provided
instruction, and any relevant JIRA context. The dictionary contains keys
such as 'summary', 'description', etc., depending on whether a
description was requested.

dict: The generated commit summary based on the staged changes, provided instruction, and any relevant JIRA context. The
dictionary contains keys such as 'summary', 'description', etc., depending on whether a description was requested.

Raises:
ValueError: If there are no changes staged for commit.
"""
Expand Down Expand Up @@ -72,19 +78,16 @@ def get_summary(self, instruction: str, generate_description: bool) -> dict:

def run(self, msg: Optional[str], edit_commit_message: bool, generate_description: bool):
"""Run the post-commit hook.

This method processes the modified files from the last commit, stages
them, and creates an auto-commit with an optional message. It also
handles JIRA integration if available. If there is an error generating
the commit summary, an exception is raised.


This method processes the modified files from the last commit, stages them, and creates an auto-commit with an optional
message. It also handles JIRA integration if available. If there is an error generating the commit summary, an exception
is raised.

Args:
msg (Optional[str]): An optional message to include in the commit.
edit_commit_message (bool): A flag indicating whether to open the git commit edit terminal after
committing.
generate_description (bool): A flag indicating whether to include a description in the commit
message.

edit_commit_message (bool): A flag indicating whether to open the git commit edit terminal after committing.
generate_description (bool): A flag indicating whether to include a description in the commit message.

Raises:
Exception: If there is an error generating the commit summary.
"""
Expand All @@ -111,18 +114,18 @@ def run(self, msg: Optional[str], edit_commit_message: bool, generate_descriptio
self._amend_commit()

def process_jira_integration(self, title: str, description: str, msg: str) -> tuple:
# Look for JIRA issue keys in commit message, title, description and user message
"""Process JIRA integration for the commit message.

Args:
title (str): Generated commit title.
description (str): Generated commit description.
msg (str): Original user message that might contain JIRA references.

Returns:
tuple: A tuple containing the updated commit title and description with
included JIRA information.
"""
# Look for JIRA issue keys in commit message, title, description and user message
issue_keys = []
if self.jira_client:
# Extract from message content
Expand Down Expand Up @@ -161,11 +164,10 @@ def process_jira_integration(self, title: str, description: str, msg: str) -> tu

def _amend_commit(self):
"""Open the default git editor for editing the commit message.

This function changes the current working directory to the repository
path, runs the git command to amend the last commit, and opens the
default editor for the user to modify the commit message. After the
operation, it returns to the original directory.

This function changes the current working directory to the repository path, runs the git command to amend the last
commit, and opens the default editor for the user to modify the commit message. After the operation, it returns to the
original directory.
"""
try:
# Change to the repository directory
Expand Down
31 changes: 14 additions & 17 deletions penify_hook/config_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@


def setup_config_parser(parent_parser):
"""Set up a configuration parser with subparsers for different types of
configurations.

This function configures and adds subcommands to the parent parser. Each
subcommand corresponds to a specific type of configuration, such as LLM
(Language Model) or JIRA. It allows users to configure settings for
these systems through command-line arguments.

# Config subcommand: Create subparsers for config types
"""Set up a configuration parser with subparsers for different types of configurations.

This function configures and adds subcommands to the parent parser. Each subcommand corresponds to a specific type of
configuration, such as LLM (Language Model) or JIRA. It allows users to configure settings for these systems through
command-line arguments.

Args:
parent_parser (argparse.ArgumentParser): The parent parser to which the config subparsers will be added.
"""

# Config subcommand: Create subparsers for config types
parser = parent_parser.add_subparsers(title="config_type", dest="config_type")

# Config subcommand: llm
Expand All @@ -39,20 +37,19 @@ def setup_config_parser(parent_parser):
# Add all other necessary arguments for config command

def handle_config(args):
"""Handle configuration settings based on the specified config type.

This function processes different types of configurations such as LLM
(Language Model) and JIRA. It saves configurations, sets up web-based
configurations, and verifies JIRA connections.

# Only import dependencies needed for config functionality here
"""Handle configuration settings based on the specified config type.

This function processes different types of configurations such as LLM (Language Model) and JIRA. It saves
configurations, sets up web-based configurations, and verifies JIRA connections.

Args:
args (argparse.Namespace): Command-line arguments containing the type of configuration to handle.

Returns:
int: Exit code indicating success or failure.
"""

# Only import dependencies needed for config functionality here
from penify_hook.commands.config_commands import save_llm_config
from penify_hook.jira_client import JiraClient # Import moved here
from penify_hook.commands.config_commands import config_jira_web, config_llm_web, save_jira_config
Expand Down
Loading
Loading