From 15c02bcf502c8f764f83f8bea43adeb2b5994767 Mon Sep 17 00:00:00 2001 From: Florian Ehrenstorfer Date: Fri, 24 Jan 2025 17:05:47 +0100 Subject: [PATCH 01/13] create detection llm endpoint --- .../app/detector/bad_practice_detector.py | 25 +++++++++++++++++++ server/intelligence-service/app/main.py | 3 +++ .../app/routers/detector.py | 22 ++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 server/intelligence-service/app/detector/bad_practice_detector.py create mode 100644 server/intelligence-service/app/routers/detector.py diff --git a/server/intelligence-service/app/detector/bad_practice_detector.py b/server/intelligence-service/app/detector/bad_practice_detector.py new file mode 100644 index 00000000..89c890c1 --- /dev/null +++ b/server/intelligence-service/app/detector/bad_practice_detector.py @@ -0,0 +1,25 @@ +from typing import List + +class PullRequest: + id: str + title: str + description: str + +class Rule: + name: str + description: str + bad_practice_id: str + +class PullRequestWithBadPractices: + pull_request_id: str + bad_practice_ids: List[str] + +def detectbadpractices(pull_requests: List[PullRequest], rules: List[Rule]) -> List[PullRequestWithBadPractices]: + bad_practices = [] + for pull_request in pull_requests: + bad_practice_ids = [] + for rule in rules: + if rule.bad_practice_id in pull_request.description: + bad_practice_ids.append(rule.bad_practice_id) + bad_practices.append(PullRequestWithBadPractices(pull_request_id=pull_request.id, bad_practice_ids=bad_practice_ids)) + return bad_practices \ No newline at end of file diff --git a/server/intelligence-service/app/main.py b/server/intelligence-service/app/main.py index 17fbcc5a..cc682d91 100644 --- a/server/intelligence-service/app/main.py +++ b/server/intelligence-service/app/main.py @@ -1,6 +1,7 @@ from fastapi import FastAPI from .routers.mentor import router as mentor_router from .routers.health import router as health_router +from .routers.detector import router as detector_router app = FastAPI( title="Hephaestus Intelligence Service API", @@ -11,3 +12,5 @@ app.include_router(mentor_router) app.include_router(health_router) + +app.include_router(detector_router) diff --git a/server/intelligence-service/app/routers/detector.py b/server/intelligence-service/app/routers/detector.py new file mode 100644 index 00000000..a7d01134 --- /dev/null +++ b/server/intelligence-service/app/routers/detector.py @@ -0,0 +1,22 @@ +from typing import List +from fastapi import APIRouter +from pydantic import BaseModel + +from app.detector.bad_practice_detector import PullRequestWithBadPractices, PullRequest, Rule, detectbadpractices + +router = APIRouter(prefix="/detector", tags=["detector"]) + +class DetectorRequest(BaseModel): + pull_requests: List[PullRequest] + rules: List[Rule] + +class DetectorResponse(BaseModel): + detectBadPractices: List[PullRequestWithBadPractices] + +@router.post( + "/", + response_model=DetectorResponse, + summary="Detect bad practices given rules.", +) +def detect(request: DetectorRequest): + return detectbadpractices(request.pull_requests, request.rules) From 8138c8bc741036cf451a3d9e0ccb7db3fc6fe337 Mon Sep 17 00:00:00 2001 From: Florian Ehrenstorfer Date: Mon, 27 Jan 2025 12:07:42 +0100 Subject: [PATCH 02/13] create detection models #226 --- .../app/detector/bad_practice_detector.py | 9 ++++++--- server/intelligence-service/app/routers/detector.py | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/server/intelligence-service/app/detector/bad_practice_detector.py b/server/intelligence-service/app/detector/bad_practice_detector.py index 89c890c1..3c68e3ea 100644 --- a/server/intelligence-service/app/detector/bad_practice_detector.py +++ b/server/intelligence-service/app/detector/bad_practice_detector.py @@ -1,16 +1,19 @@ from typing import List -class PullRequest: +from pydantic import BaseModel + + +class PullRequest(BaseModel): id: str title: str description: str -class Rule: +class Rule(BaseModel): name: str description: str bad_practice_id: str -class PullRequestWithBadPractices: +class PullRequestWithBadPractices(BaseModel): pull_request_id: str bad_practice_ids: List[str] diff --git a/server/intelligence-service/app/routers/detector.py b/server/intelligence-service/app/routers/detector.py index a7d01134..63b56fdc 100644 --- a/server/intelligence-service/app/routers/detector.py +++ b/server/intelligence-service/app/routers/detector.py @@ -1,8 +1,7 @@ from typing import List from fastapi import APIRouter from pydantic import BaseModel - -from app.detector.bad_practice_detector import PullRequestWithBadPractices, PullRequest, Rule, detectbadpractices +from ..detector.bad_practice_detector import PullRequestWithBadPractices, PullRequest, Rule, detectbadpractices router = APIRouter(prefix="/detector", tags=["detector"]) From ca3bfebc6230b49da2fb49e3773dcd22f00c7f0e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 27 Jan 2025 11:09:51 +0000 Subject: [PATCH 03/13] chore: update API specs and client --- .../intelligenceservice/api/DetectorApi.java | 117 ++++++++++++ .../model/DetectorRequest.java | 156 ++++++++++++++++ .../model/DetectorResponse.java | 116 ++++++++++++ .../model/PullRequest.java | 166 ++++++++++++++++++ .../model/PullRequestWithBadPractices.java | 146 +++++++++++++++ .../intelligenceservice/model/Rule.java | 166 ++++++++++++++++++ server/intelligence-service/openapi.yaml | 102 +++++++++++ 7 files changed, 969 insertions(+) create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequest.java create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequestWithBadPractices.java create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/Rule.java diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java new file mode 100644 index 00000000..7a6b081f --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java @@ -0,0 +1,117 @@ +package de.tum.in.www1.hephaestus.intelligenceservice.api; + +import de.tum.in.www1.hephaestus.intelligenceservice.ApiClient; +import de.tum.in.www1.hephaestus.intelligenceservice.BaseApi; + +import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorRequest; +import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorResponse; +import de.tum.in.www1.hephaestus.intelligenceservice.model.HTTPValidationError; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; + +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") +public class DetectorApi extends BaseApi { + + public DetectorApi() { + super(new ApiClient()); + } + + public DetectorApi(ApiClient apiClient) { + super(apiClient); + } + + /** + * Detect bad practices given rules. + * + *

200 - Successful Response + *

422 - Validation Error + * @param detectorRequest (required) + * @return DetectorResponse + * @throws RestClientException if an error occurs while attempting to invoke the API + */ + public DetectorResponse detectDetectorPost(DetectorRequest detectorRequest) throws RestClientException { + return detectDetectorPostWithHttpInfo(detectorRequest).getBody(); + } + + /** + * Detect bad practices given rules. + * + *

200 - Successful Response + *

422 - Validation Error + * @param detectorRequest (required) + * @return ResponseEntity<DetectorResponse> + * @throws RestClientException if an error occurs while attempting to invoke the API + */ + public ResponseEntity detectDetectorPostWithHttpInfo(DetectorRequest detectorRequest) throws RestClientException { + Object localVarPostBody = detectorRequest; + + // verify the required parameter 'detectorRequest' is set + if (detectorRequest == null) { + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'detectorRequest' when calling detectDetectorPost"); + } + + + final MultiValueMap localVarQueryParams = new LinkedMultiValueMap(); + final HttpHeaders localVarHeaderParams = new HttpHeaders(); + final MultiValueMap localVarCookieParams = new LinkedMultiValueMap(); + final MultiValueMap localVarFormParams = new LinkedMultiValueMap(); + + final String[] localVarAccepts = { + "application/json" + }; + final List localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + final String[] localVarContentTypes = { + "application/json" + }; + final MediaType localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + + String[] localVarAuthNames = new String[] { }; + + ParameterizedTypeReference localReturnType = new ParameterizedTypeReference() {}; + return apiClient.invokeAPI("/detector/", HttpMethod.POST, Collections.emptyMap(), localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType, localVarAuthNames, localReturnType); + } + + @Override + public ResponseEntity invokeAPI(String url, HttpMethod method, Object request, ParameterizedTypeReference returnType) throws RestClientException { + String localVarPath = url.replace(apiClient.getBasePath(), ""); + Object localVarPostBody = request; + + final Map uriVariables = new HashMap(); + final MultiValueMap localVarQueryParams = new LinkedMultiValueMap(); + final HttpHeaders localVarHeaderParams = new HttpHeaders(); + final MultiValueMap localVarCookieParams = new LinkedMultiValueMap(); + final MultiValueMap localVarFormParams = new LinkedMultiValueMap(); + + final String[] localVarAccepts = { + "application/json" + }; + final List localVarAccept = apiClient.selectHeaderAccept(localVarAccepts); + final String[] localVarContentTypes = { + "application/json" + }; + final MediaType localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes); + + String[] localVarAuthNames = new String[] { }; + + return apiClient.invokeAPI(localVarPath, method, uriVariables, localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType, localVarAuthNames, returnType); + } +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java new file mode 100644 index 00000000..f4bea958 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java @@ -0,0 +1,156 @@ +/* + * Hephaestus Intelligence Service API + * API documentation for the Hephaestus Intelligence Service. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: felixtj.dietrich@tum.de + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.tum.in.www1.hephaestus.intelligenceservice.model; + +import java.util.Objects; +import java.util.Arrays; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequest; +import de.tum.in.www1.hephaestus.intelligenceservice.model.Rule; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.hibernate.validator.constraints.*; + +/** + * DetectorRequest + */ +@JsonPropertyOrder({ + DetectorRequest.JSON_PROPERTY_PULL_REQUESTS, + DetectorRequest.JSON_PROPERTY_RULES +}) +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") +public class DetectorRequest { + public static final String JSON_PROPERTY_PULL_REQUESTS = "pull_requests"; + private List pullRequests = new ArrayList<>(); + + public static final String JSON_PROPERTY_RULES = "rules"; + private List rules = new ArrayList<>(); + + public DetectorRequest() { + } + + public DetectorRequest pullRequests(List pullRequests) { + + this.pullRequests = pullRequests; + return this; + } + + public DetectorRequest addPullRequestsItem(PullRequest pullRequestsItem) { + if (this.pullRequests == null) { + this.pullRequests = new ArrayList<>(); + } + this.pullRequests.add(pullRequestsItem); + return this; + } + + /** + * Get pullRequests + * @return pullRequests + */ + @jakarta.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_PULL_REQUESTS) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public List getPullRequests() { + return pullRequests; + } + + + @JsonProperty(JSON_PROPERTY_PULL_REQUESTS) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setPullRequests(List pullRequests) { + this.pullRequests = pullRequests; + } + + public DetectorRequest rules(List rules) { + + this.rules = rules; + return this; + } + + public DetectorRequest addRulesItem(Rule rulesItem) { + if (this.rules == null) { + this.rules = new ArrayList<>(); + } + this.rules.add(rulesItem); + return this; + } + + /** + * Get rules + * @return rules + */ + @jakarta.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_RULES) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public List getRules() { + return rules; + } + + + @JsonProperty(JSON_PROPERTY_RULES) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setRules(List rules) { + this.rules = rules; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DetectorRequest detectorRequest = (DetectorRequest) o; + return Objects.equals(this.pullRequests, detectorRequest.pullRequests) && + Objects.equals(this.rules, detectorRequest.rules); + } + + @Override + public int hashCode() { + return Objects.hash(pullRequests, rules); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DetectorRequest {\n"); + sb.append(" pullRequests: ").append(toIndentedString(pullRequests)).append("\n"); + sb.append(" rules: ").append(toIndentedString(rules)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java new file mode 100644 index 00000000..582db519 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java @@ -0,0 +1,116 @@ +/* + * Hephaestus Intelligence Service API + * API documentation for the Hephaestus Intelligence Service. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: felixtj.dietrich@tum.de + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.tum.in.www1.hephaestus.intelligenceservice.model; + +import java.util.Objects; +import java.util.Arrays; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequestWithBadPractices; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.hibernate.validator.constraints.*; + +/** + * DetectorResponse + */ +@JsonPropertyOrder({ + DetectorResponse.JSON_PROPERTY_DETECT_BAD_PRACTICES +}) +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") +public class DetectorResponse { + public static final String JSON_PROPERTY_DETECT_BAD_PRACTICES = "detectBadPractices"; + private List detectBadPractices = new ArrayList<>(); + + public DetectorResponse() { + } + + public DetectorResponse detectBadPractices(List detectBadPractices) { + + this.detectBadPractices = detectBadPractices; + return this; + } + + public DetectorResponse addDetectBadPracticesItem(PullRequestWithBadPractices detectBadPracticesItem) { + if (this.detectBadPractices == null) { + this.detectBadPractices = new ArrayList<>(); + } + this.detectBadPractices.add(detectBadPracticesItem); + return this; + } + + /** + * Get detectBadPractices + * @return detectBadPractices + */ + @jakarta.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_DETECT_BAD_PRACTICES) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public List getDetectBadPractices() { + return detectBadPractices; + } + + + @JsonProperty(JSON_PROPERTY_DETECT_BAD_PRACTICES) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setDetectBadPractices(List detectBadPractices) { + this.detectBadPractices = detectBadPractices; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DetectorResponse detectorResponse = (DetectorResponse) o; + return Objects.equals(this.detectBadPractices, detectorResponse.detectBadPractices); + } + + @Override + public int hashCode() { + return Objects.hash(detectBadPractices); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DetectorResponse {\n"); + sb.append(" detectBadPractices: ").append(toIndentedString(detectBadPractices)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequest.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequest.java new file mode 100644 index 00000000..f00e3624 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequest.java @@ -0,0 +1,166 @@ +/* + * Hephaestus Intelligence Service API + * API documentation for the Hephaestus Intelligence Service. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: felixtj.dietrich@tum.de + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.tum.in.www1.hephaestus.intelligenceservice.model; + +import java.util.Objects; +import java.util.Arrays; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.hibernate.validator.constraints.*; + +/** + * PullRequest + */ +@JsonPropertyOrder({ + PullRequest.JSON_PROPERTY_DESCRIPTION, + PullRequest.JSON_PROPERTY_ID, + PullRequest.JSON_PROPERTY_TITLE +}) +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") +public class PullRequest { + public static final String JSON_PROPERTY_DESCRIPTION = "description"; + private String description; + + public static final String JSON_PROPERTY_ID = "id"; + private String id; + + public static final String JSON_PROPERTY_TITLE = "title"; + private String title; + + public PullRequest() { + } + + public PullRequest description(String description) { + + this.description = description; + return this; + } + + /** + * Get description + * @return description + */ + @jakarta.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_DESCRIPTION) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getDescription() { + return description; + } + + + @JsonProperty(JSON_PROPERTY_DESCRIPTION) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setDescription(String description) { + this.description = description; + } + + public PullRequest id(String id) { + + this.id = id; + return this; + } + + /** + * Get id + * @return id + */ + @jakarta.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_ID) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getId() { + return id; + } + + + @JsonProperty(JSON_PROPERTY_ID) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setId(String id) { + this.id = id; + } + + public PullRequest title(String title) { + + this.title = title; + return this; + } + + /** + * Get title + * @return title + */ + @jakarta.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_TITLE) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getTitle() { + return title; + } + + + @JsonProperty(JSON_PROPERTY_TITLE) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setTitle(String title) { + this.title = title; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PullRequest pullRequest = (PullRequest) o; + return Objects.equals(this.description, pullRequest.description) && + Objects.equals(this.id, pullRequest.id) && + Objects.equals(this.title, pullRequest.title); + } + + @Override + public int hashCode() { + return Objects.hash(description, id, title); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PullRequest {\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" title: ").append(toIndentedString(title)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequestWithBadPractices.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequestWithBadPractices.java new file mode 100644 index 00000000..7993f037 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequestWithBadPractices.java @@ -0,0 +1,146 @@ +/* + * Hephaestus Intelligence Service API + * API documentation for the Hephaestus Intelligence Service. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: felixtj.dietrich@tum.de + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.tum.in.www1.hephaestus.intelligenceservice.model; + +import java.util.Objects; +import java.util.Arrays; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.hibernate.validator.constraints.*; + +/** + * PullRequestWithBadPractices + */ +@JsonPropertyOrder({ + PullRequestWithBadPractices.JSON_PROPERTY_BAD_PRACTICE_IDS, + PullRequestWithBadPractices.JSON_PROPERTY_PULL_REQUEST_ID +}) +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") +public class PullRequestWithBadPractices { + public static final String JSON_PROPERTY_BAD_PRACTICE_IDS = "bad_practice_ids"; + private List badPracticeIds = new ArrayList<>(); + + public static final String JSON_PROPERTY_PULL_REQUEST_ID = "pull_request_id"; + private String pullRequestId; + + public PullRequestWithBadPractices() { + } + + public PullRequestWithBadPractices badPracticeIds(List badPracticeIds) { + + this.badPracticeIds = badPracticeIds; + return this; + } + + public PullRequestWithBadPractices addBadPracticeIdsItem(String badPracticeIdsItem) { + if (this.badPracticeIds == null) { + this.badPracticeIds = new ArrayList<>(); + } + this.badPracticeIds.add(badPracticeIdsItem); + return this; + } + + /** + * Get badPracticeIds + * @return badPracticeIds + */ + @jakarta.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_BAD_PRACTICE_IDS) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public List getBadPracticeIds() { + return badPracticeIds; + } + + + @JsonProperty(JSON_PROPERTY_BAD_PRACTICE_IDS) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setBadPracticeIds(List badPracticeIds) { + this.badPracticeIds = badPracticeIds; + } + + public PullRequestWithBadPractices pullRequestId(String pullRequestId) { + + this.pullRequestId = pullRequestId; + return this; + } + + /** + * Get pullRequestId + * @return pullRequestId + */ + @jakarta.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_PULL_REQUEST_ID) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getPullRequestId() { + return pullRequestId; + } + + + @JsonProperty(JSON_PROPERTY_PULL_REQUEST_ID) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setPullRequestId(String pullRequestId) { + this.pullRequestId = pullRequestId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PullRequestWithBadPractices pullRequestWithBadPractices = (PullRequestWithBadPractices) o; + return Objects.equals(this.badPracticeIds, pullRequestWithBadPractices.badPracticeIds) && + Objects.equals(this.pullRequestId, pullRequestWithBadPractices.pullRequestId); + } + + @Override + public int hashCode() { + return Objects.hash(badPracticeIds, pullRequestId); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PullRequestWithBadPractices {\n"); + sb.append(" badPracticeIds: ").append(toIndentedString(badPracticeIds)).append("\n"); + sb.append(" pullRequestId: ").append(toIndentedString(pullRequestId)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/Rule.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/Rule.java new file mode 100644 index 00000000..22e0111e --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/Rule.java @@ -0,0 +1,166 @@ +/* + * Hephaestus Intelligence Service API + * API documentation for the Hephaestus Intelligence Service. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: felixtj.dietrich@tum.de + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.tum.in.www1.hephaestus.intelligenceservice.model; + +import java.util.Objects; +import java.util.Arrays; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.hibernate.validator.constraints.*; + +/** + * Rule + */ +@JsonPropertyOrder({ + Rule.JSON_PROPERTY_BAD_PRACTICE_ID, + Rule.JSON_PROPERTY_DESCRIPTION, + Rule.JSON_PROPERTY_NAME +}) +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") +public class Rule { + public static final String JSON_PROPERTY_BAD_PRACTICE_ID = "bad_practice_id"; + private String badPracticeId; + + public static final String JSON_PROPERTY_DESCRIPTION = "description"; + private String description; + + public static final String JSON_PROPERTY_NAME = "name"; + private String name; + + public Rule() { + } + + public Rule badPracticeId(String badPracticeId) { + + this.badPracticeId = badPracticeId; + return this; + } + + /** + * Get badPracticeId + * @return badPracticeId + */ + @jakarta.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_BAD_PRACTICE_ID) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getBadPracticeId() { + return badPracticeId; + } + + + @JsonProperty(JSON_PROPERTY_BAD_PRACTICE_ID) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setBadPracticeId(String badPracticeId) { + this.badPracticeId = badPracticeId; + } + + public Rule description(String description) { + + this.description = description; + return this; + } + + /** + * Get description + * @return description + */ + @jakarta.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_DESCRIPTION) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getDescription() { + return description; + } + + + @JsonProperty(JSON_PROPERTY_DESCRIPTION) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setDescription(String description) { + this.description = description; + } + + public Rule name(String name) { + + this.name = name; + return this; + } + + /** + * Get name + * @return name + */ + @jakarta.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_NAME) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + + public String getName() { + return name; + } + + + @JsonProperty(JSON_PROPERTY_NAME) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Rule rule = (Rule) o; + return Objects.equals(this.badPracticeId, rule.badPracticeId) && + Objects.equals(this.description, rule.description) && + Objects.equals(this.name, rule.name); + } + + @Override + public int hashCode() { + return Objects.hash(badPracticeId, description, name); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Rule {\n"); + sb.append(" badPracticeId: ").append(toIndentedString(badPracticeId)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/server/intelligence-service/openapi.yaml b/server/intelligence-service/openapi.yaml index 964034da..30f186af 100644 --- a/server/intelligence-service/openapi.yaml +++ b/server/intelligence-service/openapi.yaml @@ -1,5 +1,33 @@ components: schemas: + DetectorRequest: + properties: + pull_requests: + items: + $ref: '#/components/schemas/PullRequest' + title: Pull Requests + type: array + rules: + items: + $ref: '#/components/schemas/Rule' + title: Rules + type: array + required: + - pull_requests + - rules + title: DetectorRequest + type: object + DetectorResponse: + properties: + detectBadPractices: + items: + $ref: '#/components/schemas/PullRequestWithBadPractices' + title: Detectbadpractices + type: array + required: + - detectBadPractices + title: DetectorResponse + type: object HTTPValidationError: properties: detail: @@ -58,6 +86,55 @@ components: - dev_progress title: MentorStartRequest type: object + PullRequest: + properties: + description: + title: Description + type: string + id: + title: Id + type: string + title: + title: Title + type: string + required: + - id + - title + - description + title: PullRequest + type: object + PullRequestWithBadPractices: + properties: + bad_practice_ids: + items: + type: string + title: Bad Practice Ids + type: array + pull_request_id: + title: Pull Request Id + type: string + required: + - pull_request_id + - bad_practice_ids + title: PullRequestWithBadPractices + type: object + Rule: + properties: + bad_practice_id: + title: Bad Practice Id + type: string + description: + title: Description + type: string + name: + title: Name + type: string + required: + - name + - description + - bad_practice_id + title: Rule + type: object ValidationError: properties: loc: @@ -88,6 +165,31 @@ info: version: 0.0.1 openapi: 3.1.0 paths: + /detector/: + post: + operationId: detect_detector__post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DetectorRequest' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/DetectorResponse' + description: Successful Response + '422': + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + description: Validation Error + summary: Detect bad practices given rules. + tags: + - detector /health: get: description: "## Perform a Health Check\nEndpoint to perform a healthcheck on.\ From 54200c4a27f3e6e0d1241682b1400a4ff6834925 Mon Sep 17 00:00:00 2001 From: Florian Ehrenstorfer Date: Mon, 27 Jan 2025 16:03:40 +0100 Subject: [PATCH 04/13] create rule based detection of bad practices(#226) --- .../hephaestus/activity/ActivityService.java | 6 +- .../PullRequestBadPracticeRuleRepository.java | 19 ++++ .../PullRequestBadPracticeDetector.java | 95 ++++++++++++++++++- .../activity/model/BadPracticeType.java | 28 ++++++ .../model/PullRequestBadPracticeRule.java | 0 5 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/PullRequestBadPracticeRuleRepository.java create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/BadPracticeType.java create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPracticeRule.java diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java index dd7cf9f4..41ebe795 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java @@ -85,10 +85,10 @@ public List detectBadPractices(String login) { Set.of(Issue.State.OPEN) ); - return pullRequests + List badPractices = pullRequestBadPracticeDetector.detectAndSyncBadPractices(pullRequests); + + return badPractices .stream() - .map(pullRequestBadPracticeDetector::detectAndSyncBadPractices) - .flatMap(List::stream) .map(PullRequestBadPracticeDTO::fromPullRequestBadPractice) .collect(Collectors.toList()); } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/PullRequestBadPracticeRuleRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/PullRequestBadPracticeRuleRepository.java new file mode 100644 index 00000000..c1bf4dd0 --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/PullRequestBadPracticeRuleRepository.java @@ -0,0 +1,19 @@ +package de.tum.in.www1.hephaestus.activity; + +import de.tum.in.www1.hephaestus.activity.model.PullRequestBadPracticeRule; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +@Repository +public interface PullRequestBadPracticeRuleRepository extends JpaRepository { + @Query( + """ + SELECT prbp + FROM PullRequestBadPracticeRule prbp + WHERE prbp.active = true + """ + ) + List findAllActive(); +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java index 385d9ecf..2d86ead6 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java @@ -1,9 +1,22 @@ package de.tum.in.www1.hephaestus.activity.badpracticedetector; import de.tum.in.www1.hephaestus.activity.PullRequestBadPracticeRepository; +import de.tum.in.www1.hephaestus.activity.PullRequestBadPracticeRuleRepository; +import de.tum.in.www1.hephaestus.activity.model.BadPracticeType; import de.tum.in.www1.hephaestus.activity.model.PullRequestBadPractice; +import de.tum.in.www1.hephaestus.activity.model.PullRequestBadPracticeRule; import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest; + +import java.util.LinkedList; import java.util.List; +import java.util.stream.Collectors; + +import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestRepository; +import de.tum.in.www1.hephaestus.intelligenceservice.api.DetectorApi; +import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorRequest; +import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorResponse; +import de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequestWithBadPractices; +import de.tum.in.www1.hephaestus.intelligenceservice.model.Rule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -14,16 +27,90 @@ public class PullRequestBadPracticeDetector { private static final Logger logger = LoggerFactory.getLogger(PullRequestBadPracticeDetector.class); + @Autowired + private PullRequestRepository pullRequestRepository; + @Autowired private PullRequestBadPracticeRepository pullRequestBadPracticeRepository; - public List detectAndSyncBadPractices(PullRequest pullRequest) { - logger.info("Detecting bad practices for pull request: {}.", pullRequest.getId()); + @Autowired + private PullRequestBadPracticeRuleRepository pullRequestBadPracticeRuleRepository; + + private final DetectorApi detectorApi = new DetectorApi(); + + public List detectAndSyncBadPractices(List pullRequests) { + logger.info("Detecting bad practices for pull requests."); + + List rules = pullRequestBadPracticeRuleRepository.findAllActive(); + + DetectorRequest detectorRequest = new DetectorRequest(); + detectorRequest.setPullRequests(mapToApiPullRequests(pullRequests)); + detectorRequest.setRules(mapToApiRules(rules)); + DetectorResponse detectorResponse = detectorApi.detectDetectorPost(detectorRequest); + + List detectedBadPractices = new LinkedList<>(); + + detectorResponse.getDetectBadPractices().forEach(prWithBadPractices -> + detectedBadPractices.addAll(handleDetectedBadPractices(prWithBadPractices)) + ); + + return detectedBadPractices; + } + + private List handleDetectedBadPractices(PullRequestWithBadPractices prWithBadPractices) { + + Long pullRequestId = Long.valueOf(prWithBadPractices.getPullRequestId()); + PullRequest pullRequest = pullRequestRepository.findById(pullRequestId).orElse(null); + + if (pullRequest == null) { + logger.error("Pull request with id {} not found.", prWithBadPractices.getPullRequestId()); + return List.of(); + } List existingBadPractices = pullRequestBadPracticeRepository.findByPullRequestId( - pullRequest.getId() + pullRequest.getId() ); - return existingBadPractices; + List newBadPractices = prWithBadPractices.getBadPracticeIds().stream() + .map(badPracticeRule -> { + PullRequestBadPractice pullRequestBadPractice = new PullRequestBadPractice(); + pullRequestBadPractice.setPullrequest(pullRequest); + pullRequestBadPractice.setType(findBadPracticeType(badPracticeRule)); + pullRequestBadPractice.setResolved(false); + return pullRequestBadPractice; + }) + .collect(Collectors.toList()); + + pullRequestBadPracticeRepository.saveAll(newBadPractices); + existingBadPractices.removeAll(newBadPractices); + existingBadPractices.forEach(badPractice -> badPractice.setResolved(true)); + pullRequestBadPracticeRepository.saveAll(existingBadPractices); + return newBadPractices; + } + + private BadPracticeType findBadPracticeType(String badPracticeRuleId) { + return pullRequestBadPracticeRuleRepository.findById(Long.valueOf(badPracticeRuleId)) + .map(PullRequestBadPracticeRule::getType) + .orElse(null); + } + + private List mapToApiPullRequests(List pullRequests) { + return pullRequests.stream().map(pullRequest -> { + de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequest apiPullRequest = new de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequest(); + apiPullRequest.setId(String.valueOf(pullRequest.getId())); + apiPullRequest.setTitle(pullRequest.getTitle()); + apiPullRequest.setDescription(pullRequest.getBody()); + return apiPullRequest; + }).collect(Collectors.toList()); + } + + private List mapToApiRules(List rules) { + return rules.stream().map(rule -> { + Rule apiRule = new Rule(); + apiRule.setBadPracticeId(String.valueOf(rule.getId())); + apiRule.setName(rule.getType().getTitle()); + apiRule.setDescription(rule.getType().getLlmPrompt()); + return apiRule; + }).collect(Collectors.toList()); } } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/BadPracticeType.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/BadPracticeType.java new file mode 100644 index 00000000..45df85eb --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/BadPracticeType.java @@ -0,0 +1,28 @@ +package de.tum.in.www1.hephaestus.activity.model; + +import lombok.Getter; + +@Getter +public enum BadPracticeType { + + UncheckedCheckbox( + "Unchecked Checkbox", + "This pull request contains an unchecked checkbox.", + "The pull request contains an unchecked checkbox. An unchecked checkbox looks like this: '- [ ]'." + ), + EmptySection( + "Empty Section", + "This pull request contains an empty section.", + "The pull request contains an empty section. An empty section is a section that does not contain any content. An empty section is directly followed by another section of the same or bigger title size." + ); + + private final String title; + private final String description; + private final String llmPrompt; + + BadPracticeType(String title, String description, String llmPrompt) { + this.title = title; + this.description = description; + this.llmPrompt = llmPrompt; + } +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPracticeRule.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPracticeRule.java new file mode 100644 index 00000000..e69de29b From 8ad1f939a6ee7cef6be26517443e1cf5f91d3cb1 Mon Sep 17 00:00:00 2001 From: Florian Ehrenstorfer Date: Tue, 28 Jan 2025 17:30:37 +0100 Subject: [PATCH 05/13] rework llm based detection #226 --- .../PullRequestBadPracticeRuleRepository.java | 19 -------- .../PullRequestBadPracticeDetector.java | 10 ----- .../activity/model/BadPracticeType.java | 28 ------------ .../model/PullRequestBadPracticeRule.java | 0 .../app/detector/bad_practice_detector.py | 43 +++++++++++-------- .../prompts/pullrequest_badpractice_detector | 28 ++++++++++++ .../app/routers/detector.py | 17 ++------ 7 files changed, 56 insertions(+), 89 deletions(-) delete mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/PullRequestBadPracticeRuleRepository.java delete mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/BadPracticeType.java delete mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPracticeRule.java create mode 100644 server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/PullRequestBadPracticeRuleRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/PullRequestBadPracticeRuleRepository.java deleted file mode 100644 index c1bf4dd0..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/PullRequestBadPracticeRuleRepository.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.tum.in.www1.hephaestus.activity; - -import de.tum.in.www1.hephaestus.activity.model.PullRequestBadPracticeRule; -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; - -@Repository -public interface PullRequestBadPracticeRuleRepository extends JpaRepository { - @Query( - """ - SELECT prbp - FROM PullRequestBadPracticeRule prbp - WHERE prbp.active = true - """ - ) - List findAllActive(); -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java index 2d86ead6..bd24079f 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java @@ -1,12 +1,8 @@ package de.tum.in.www1.hephaestus.activity.badpracticedetector; import de.tum.in.www1.hephaestus.activity.PullRequestBadPracticeRepository; -import de.tum.in.www1.hephaestus.activity.PullRequestBadPracticeRuleRepository; -import de.tum.in.www1.hephaestus.activity.model.BadPracticeType; import de.tum.in.www1.hephaestus.activity.model.PullRequestBadPractice; -import de.tum.in.www1.hephaestus.activity.model.PullRequestBadPracticeRule; import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest; - import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -33,19 +29,14 @@ public class PullRequestBadPracticeDetector { @Autowired private PullRequestBadPracticeRepository pullRequestBadPracticeRepository; - @Autowired - private PullRequestBadPracticeRuleRepository pullRequestBadPracticeRuleRepository; - private final DetectorApi detectorApi = new DetectorApi(); public List detectAndSyncBadPractices(List pullRequests) { logger.info("Detecting bad practices for pull requests."); - List rules = pullRequestBadPracticeRuleRepository.findAllActive(); DetectorRequest detectorRequest = new DetectorRequest(); detectorRequest.setPullRequests(mapToApiPullRequests(pullRequests)); - detectorRequest.setRules(mapToApiRules(rules)); DetectorResponse detectorResponse = detectorApi.detectDetectorPost(detectorRequest); List detectedBadPractices = new LinkedList<>(); @@ -75,7 +66,6 @@ private List handleDetectedBadPractices(PullRequestWithB .map(badPracticeRule -> { PullRequestBadPractice pullRequestBadPractice = new PullRequestBadPractice(); pullRequestBadPractice.setPullrequest(pullRequest); - pullRequestBadPractice.setType(findBadPracticeType(badPracticeRule)); pullRequestBadPractice.setResolved(false); return pullRequestBadPractice; }) diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/BadPracticeType.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/BadPracticeType.java deleted file mode 100644 index 45df85eb..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/BadPracticeType.java +++ /dev/null @@ -1,28 +0,0 @@ -package de.tum.in.www1.hephaestus.activity.model; - -import lombok.Getter; - -@Getter -public enum BadPracticeType { - - UncheckedCheckbox( - "Unchecked Checkbox", - "This pull request contains an unchecked checkbox.", - "The pull request contains an unchecked checkbox. An unchecked checkbox looks like this: '- [ ]'." - ), - EmptySection( - "Empty Section", - "This pull request contains an empty section.", - "The pull request contains an empty section. An empty section is a section that does not contain any content. An empty section is directly followed by another section of the same or bigger title size." - ); - - private final String title; - private final String description; - private final String llmPrompt; - - BadPracticeType(String title, String description, String llmPrompt) { - this.title = title; - this.description = description; - this.llmPrompt = llmPrompt; - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPracticeRule.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPracticeRule.java deleted file mode 100644 index e69de29b..00000000 diff --git a/server/intelligence-service/app/detector/bad_practice_detector.py b/server/intelligence-service/app/detector/bad_practice_detector.py index 3c68e3ea..40102d5c 100644 --- a/server/intelligence-service/app/detector/bad_practice_detector.py +++ b/server/intelligence-service/app/detector/bad_practice_detector.py @@ -1,6 +1,9 @@ from typing import List -from pydantic import BaseModel +from langchain_core.prompts import PromptTemplate, ChatPromptTemplate +from pydantic import BaseModel, Field + +from ..model import model class PullRequest(BaseModel): @@ -8,21 +11,23 @@ class PullRequest(BaseModel): title: str description: str -class Rule(BaseModel): - name: str - description: str - bad_practice_id: str - -class PullRequestWithBadPractices(BaseModel): - pull_request_id: str - bad_practice_ids: List[str] - -def detectbadpractices(pull_requests: List[PullRequest], rules: List[Rule]) -> List[PullRequestWithBadPractices]: - bad_practices = [] - for pull_request in pull_requests: - bad_practice_ids = [] - for rule in rules: - if rule.bad_practice_id in pull_request.description: - bad_practice_ids.append(rule.bad_practice_id) - bad_practices.append(PullRequestWithBadPractices(pull_request_id=pull_request.id, bad_practice_ids=bad_practice_ids)) - return bad_practices \ No newline at end of file +class BadPractice(BaseModel): + """A detected bad practice in a pull request.""" + + title: str = Field(description="The title of the bad practice.") + description: str = Field(description="The description of the bad practice.") + +class BadPracticeList(BaseModel): + """A list of bad practices detected in a pull request.""" + + bad_practices: List[BadPractice] = Field(description="A list of bad practices detected in a pull request.") + + +def detectbadpractices(pull_requests: PullRequest) -> BadPracticeList: + with open("./prompts/pullrequest_badpractice_detector.txt", "r") as f: + prompt_text = f.read() + prompt_template = ChatPromptTemplate.from_template(prompt_text) + prompt = prompt_template.invoke(title=pull_requests.title, description=pull_requests.description) + structured_llm = model.with_structured_output(BadPracticeList) + response = structured_llm.invoke(prompt) + return response \ No newline at end of file diff --git a/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector b/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector new file mode 100644 index 00000000..1aa76e54 --- /dev/null +++ b/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector @@ -0,0 +1,28 @@ +You are a bad practice detector reviewing pull requests for bad practices. +You analyze and review the title and description of the pull request to identify any bad practices. +You detect bad practices based on guidelines for good pull request titles and descriptions. + +PRIMARY TASK: +Detect and identify any bad practices in the provided pull request title and description. +- Review the title and description for any issues or violations of the guidelines. +- For each detected bad practice, provide a title and a brief description of the issue. +- Return a list of all detected bad practices in the pull request. + +GUIDELINES: +1. The title and description should be clear, concise, and descriptive. +2. The title should be specific and summarize the changes made in the pull request. +3. The description should provide additional context and details about the changes. +4. The title and description should be free of spelling and grammatical errors. +5. The title and description should follow the project's guidelines and conventions. +6. The description should not contain empty sections or placeholder text. +7. The description should not include unchecked checkboxes or unresolved action items. +8. The description should not include open TODOs +9. In the description the motivation and description sections should be filled out. +10. The description should not be empty. + +REQUIREMENTS: +1. Identify and describe all bad practices in the pull request title and description. +2. Provide a clear title and a brief and concise description for each detected bad practice. + +Pull Request Title: {title} +Pull Request Description: {description} \ No newline at end of file diff --git a/server/intelligence-service/app/routers/detector.py b/server/intelligence-service/app/routers/detector.py index 63b56fdc..9b2be4cf 100644 --- a/server/intelligence-service/app/routers/detector.py +++ b/server/intelligence-service/app/routers/detector.py @@ -1,21 +1,12 @@ -from typing import List from fastapi import APIRouter -from pydantic import BaseModel -from ..detector.bad_practice_detector import PullRequestWithBadPractices, PullRequest, Rule, detectbadpractices +from ..detector.bad_practice_detector import PullRequest, detectbadpractices, BadPracticeList router = APIRouter(prefix="/detector", tags=["detector"]) -class DetectorRequest(BaseModel): - pull_requests: List[PullRequest] - rules: List[Rule] - -class DetectorResponse(BaseModel): - detectBadPractices: List[PullRequestWithBadPractices] - @router.post( "/", - response_model=DetectorResponse, + response_model=BadPracticeList, summary="Detect bad practices given rules.", ) -def detect(request: DetectorRequest): - return detectbadpractices(request.pull_requests, request.rules) +def detect(request: PullRequest): + return detectbadpractices(request) From 08515143ea2dff4077585987401f2b9cf363081a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 Jan 2025 16:35:32 +0000 Subject: [PATCH 06/13] chore: update API specs and client --- .../intelligenceservice/api/DetectorApi.java | 28 ++-- .../model/{Rule.java => BadPractice.java} | 81 +++------ ...ctorResponse.java => BadPracticeList.java} | 52 +++--- .../model/DetectorRequest.java | 156 ------------------ .../model/PullRequestWithBadPractices.java | 146 ---------------- server/intelligence-service/openapi.yaml | 77 +++------ 6 files changed, 88 insertions(+), 452 deletions(-) rename server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/{Rule.java => BadPractice.java} (59%) rename server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/{DetectorResponse.java => BadPracticeList.java} (56%) delete mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java delete mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequestWithBadPractices.java diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java index 7a6b081f..7f3ccca8 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java @@ -3,9 +3,9 @@ import de.tum.in.www1.hephaestus.intelligenceservice.ApiClient; import de.tum.in.www1.hephaestus.intelligenceservice.BaseApi; -import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorRequest; -import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorResponse; +import de.tum.in.www1.hephaestus.intelligenceservice.model.BadPracticeList; import de.tum.in.www1.hephaestus.intelligenceservice.model.HTTPValidationError; +import de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequest; import java.util.Collections; import java.util.HashMap; @@ -44,12 +44,12 @@ public DetectorApi(ApiClient apiClient) { * *

200 - Successful Response *

422 - Validation Error - * @param detectorRequest (required) - * @return DetectorResponse + * @param pullRequest (required) + * @return BadPracticeList * @throws RestClientException if an error occurs while attempting to invoke the API */ - public DetectorResponse detectDetectorPost(DetectorRequest detectorRequest) throws RestClientException { - return detectDetectorPostWithHttpInfo(detectorRequest).getBody(); + public BadPracticeList detectDetectorPost(PullRequest pullRequest) throws RestClientException { + return detectDetectorPostWithHttpInfo(pullRequest).getBody(); } /** @@ -57,16 +57,16 @@ public DetectorResponse detectDetectorPost(DetectorRequest detectorRequest) thro * *

200 - Successful Response *

422 - Validation Error - * @param detectorRequest (required) - * @return ResponseEntity<DetectorResponse> + * @param pullRequest (required) + * @return ResponseEntity<BadPracticeList> * @throws RestClientException if an error occurs while attempting to invoke the API */ - public ResponseEntity detectDetectorPostWithHttpInfo(DetectorRequest detectorRequest) throws RestClientException { - Object localVarPostBody = detectorRequest; + public ResponseEntity detectDetectorPostWithHttpInfo(PullRequest pullRequest) throws RestClientException { + Object localVarPostBody = pullRequest; - // verify the required parameter 'detectorRequest' is set - if (detectorRequest == null) { - throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'detectorRequest' when calling detectDetectorPost"); + // verify the required parameter 'pullRequest' is set + if (pullRequest == null) { + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'pullRequest' when calling detectDetectorPost"); } @@ -86,7 +86,7 @@ public ResponseEntity detectDetectorPostWithHttpInfo(DetectorR String[] localVarAuthNames = new String[] { }; - ParameterizedTypeReference localReturnType = new ParameterizedTypeReference() {}; + ParameterizedTypeReference localReturnType = new ParameterizedTypeReference() {}; return apiClient.invokeAPI("/detector/", HttpMethod.POST, Collections.emptyMap(), localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType, localVarAuthNames, localReturnType); } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/Rule.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/BadPractice.java similarity index 59% rename from server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/Rule.java rename to server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/BadPractice.java index 22e0111e..d251392e 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/Rule.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/BadPractice.java @@ -25,60 +25,31 @@ import org.hibernate.validator.constraints.*; /** - * Rule + * A detected bad practice in a pull request. */ @JsonPropertyOrder({ - Rule.JSON_PROPERTY_BAD_PRACTICE_ID, - Rule.JSON_PROPERTY_DESCRIPTION, - Rule.JSON_PROPERTY_NAME + BadPractice.JSON_PROPERTY_DESCRIPTION, + BadPractice.JSON_PROPERTY_TITLE }) @jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") -public class Rule { - public static final String JSON_PROPERTY_BAD_PRACTICE_ID = "bad_practice_id"; - private String badPracticeId; - +public class BadPractice { public static final String JSON_PROPERTY_DESCRIPTION = "description"; private String description; - public static final String JSON_PROPERTY_NAME = "name"; - private String name; - - public Rule() { - } - - public Rule badPracticeId(String badPracticeId) { - - this.badPracticeId = badPracticeId; - return this; - } - - /** - * Get badPracticeId - * @return badPracticeId - */ - @jakarta.annotation.Nonnull - @JsonProperty(JSON_PROPERTY_BAD_PRACTICE_ID) - @JsonInclude(value = JsonInclude.Include.ALWAYS) + public static final String JSON_PROPERTY_TITLE = "title"; + private String title; - public String getBadPracticeId() { - return badPracticeId; - } - - - @JsonProperty(JSON_PROPERTY_BAD_PRACTICE_ID) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setBadPracticeId(String badPracticeId) { - this.badPracticeId = badPracticeId; + public BadPractice() { } - public Rule description(String description) { + public BadPractice description(String description) { this.description = description; return this; } /** - * Get description + * The description of the bad practice. * @return description */ @jakarta.annotation.Nonnull @@ -96,29 +67,29 @@ public void setDescription(String description) { this.description = description; } - public Rule name(String name) { + public BadPractice title(String title) { - this.name = name; + this.title = title; return this; } /** - * Get name - * @return name + * The title of the bad practice. + * @return title */ @jakarta.annotation.Nonnull - @JsonProperty(JSON_PROPERTY_NAME) + @JsonProperty(JSON_PROPERTY_TITLE) @JsonInclude(value = JsonInclude.Include.ALWAYS) - public String getName() { - return name; + public String getTitle() { + return title; } - @JsonProperty(JSON_PROPERTY_NAME) + @JsonProperty(JSON_PROPERTY_TITLE) @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setName(String name) { - this.name = name; + public void setTitle(String title) { + this.title = title; } @Override @@ -129,24 +100,22 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - Rule rule = (Rule) o; - return Objects.equals(this.badPracticeId, rule.badPracticeId) && - Objects.equals(this.description, rule.description) && - Objects.equals(this.name, rule.name); + BadPractice badPractice = (BadPractice) o; + return Objects.equals(this.description, badPractice.description) && + Objects.equals(this.title, badPractice.title); } @Override public int hashCode() { - return Objects.hash(badPracticeId, description, name); + return Objects.hash(description, title); } @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("class Rule {\n"); - sb.append(" badPracticeId: ").append(toIndentedString(badPracticeId)).append("\n"); + sb.append("class BadPractice {\n"); sb.append(" description: ").append(toIndentedString(description)).append("\n"); - sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" title: ").append(toIndentedString(title)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/BadPracticeList.java similarity index 56% rename from server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java rename to server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/BadPracticeList.java index 582db519..a55b7f33 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/BadPracticeList.java @@ -20,7 +20,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonValue; -import de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequestWithBadPractices; +import de.tum.in.www1.hephaestus.intelligenceservice.model.BadPractice; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -29,50 +29,50 @@ import org.hibernate.validator.constraints.*; /** - * DetectorResponse + * A list of bad practices detected in a pull request. */ @JsonPropertyOrder({ - DetectorResponse.JSON_PROPERTY_DETECT_BAD_PRACTICES + BadPracticeList.JSON_PROPERTY_BAD_PRACTICES }) @jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") -public class DetectorResponse { - public static final String JSON_PROPERTY_DETECT_BAD_PRACTICES = "detectBadPractices"; - private List detectBadPractices = new ArrayList<>(); +public class BadPracticeList { + public static final String JSON_PROPERTY_BAD_PRACTICES = "bad_practices"; + private List badPractices = new ArrayList<>(); - public DetectorResponse() { + public BadPracticeList() { } - public DetectorResponse detectBadPractices(List detectBadPractices) { + public BadPracticeList badPractices(List badPractices) { - this.detectBadPractices = detectBadPractices; + this.badPractices = badPractices; return this; } - public DetectorResponse addDetectBadPracticesItem(PullRequestWithBadPractices detectBadPracticesItem) { - if (this.detectBadPractices == null) { - this.detectBadPractices = new ArrayList<>(); + public BadPracticeList addBadPracticesItem(BadPractice badPracticesItem) { + if (this.badPractices == null) { + this.badPractices = new ArrayList<>(); } - this.detectBadPractices.add(detectBadPracticesItem); + this.badPractices.add(badPracticesItem); return this; } /** - * Get detectBadPractices - * @return detectBadPractices + * A list of bad practices detected in a pull request. + * @return badPractices */ @jakarta.annotation.Nonnull - @JsonProperty(JSON_PROPERTY_DETECT_BAD_PRACTICES) + @JsonProperty(JSON_PROPERTY_BAD_PRACTICES) @JsonInclude(value = JsonInclude.Include.ALWAYS) - public List getDetectBadPractices() { - return detectBadPractices; + public List getBadPractices() { + return badPractices; } - @JsonProperty(JSON_PROPERTY_DETECT_BAD_PRACTICES) + @JsonProperty(JSON_PROPERTY_BAD_PRACTICES) @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setDetectBadPractices(List detectBadPractices) { - this.detectBadPractices = detectBadPractices; + public void setBadPractices(List badPractices) { + this.badPractices = badPractices; } @Override @@ -83,20 +83,20 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - DetectorResponse detectorResponse = (DetectorResponse) o; - return Objects.equals(this.detectBadPractices, detectorResponse.detectBadPractices); + BadPracticeList badPracticeList = (BadPracticeList) o; + return Objects.equals(this.badPractices, badPracticeList.badPractices); } @Override public int hashCode() { - return Objects.hash(detectBadPractices); + return Objects.hash(badPractices); } @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("class DetectorResponse {\n"); - sb.append(" detectBadPractices: ").append(toIndentedString(detectBadPractices)).append("\n"); + sb.append("class BadPracticeList {\n"); + sb.append(" badPractices: ").append(toIndentedString(badPractices)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java deleted file mode 100644 index f4bea958..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Hephaestus Intelligence Service API - * API documentation for the Hephaestus Intelligence Service. - * - * The version of the OpenAPI document: 0.0.1 - * Contact: felixtj.dietrich@tum.de - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -package de.tum.in.www1.hephaestus.intelligenceservice.model; - -import java.util.Objects; -import java.util.Arrays; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.annotation.JsonValue; -import de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequest; -import de.tum.in.www1.hephaestus.intelligenceservice.model.Rule; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonTypeName; -import org.hibernate.validator.constraints.*; - -/** - * DetectorRequest - */ -@JsonPropertyOrder({ - DetectorRequest.JSON_PROPERTY_PULL_REQUESTS, - DetectorRequest.JSON_PROPERTY_RULES -}) -@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") -public class DetectorRequest { - public static final String JSON_PROPERTY_PULL_REQUESTS = "pull_requests"; - private List pullRequests = new ArrayList<>(); - - public static final String JSON_PROPERTY_RULES = "rules"; - private List rules = new ArrayList<>(); - - public DetectorRequest() { - } - - public DetectorRequest pullRequests(List pullRequests) { - - this.pullRequests = pullRequests; - return this; - } - - public DetectorRequest addPullRequestsItem(PullRequest pullRequestsItem) { - if (this.pullRequests == null) { - this.pullRequests = new ArrayList<>(); - } - this.pullRequests.add(pullRequestsItem); - return this; - } - - /** - * Get pullRequests - * @return pullRequests - */ - @jakarta.annotation.Nonnull - @JsonProperty(JSON_PROPERTY_PULL_REQUESTS) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - - public List getPullRequests() { - return pullRequests; - } - - - @JsonProperty(JSON_PROPERTY_PULL_REQUESTS) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setPullRequests(List pullRequests) { - this.pullRequests = pullRequests; - } - - public DetectorRequest rules(List rules) { - - this.rules = rules; - return this; - } - - public DetectorRequest addRulesItem(Rule rulesItem) { - if (this.rules == null) { - this.rules = new ArrayList<>(); - } - this.rules.add(rulesItem); - return this; - } - - /** - * Get rules - * @return rules - */ - @jakarta.annotation.Nonnull - @JsonProperty(JSON_PROPERTY_RULES) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - - public List getRules() { - return rules; - } - - - @JsonProperty(JSON_PROPERTY_RULES) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setRules(List rules) { - this.rules = rules; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - DetectorRequest detectorRequest = (DetectorRequest) o; - return Objects.equals(this.pullRequests, detectorRequest.pullRequests) && - Objects.equals(this.rules, detectorRequest.rules); - } - - @Override - public int hashCode() { - return Objects.hash(pullRequests, rules); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class DetectorRequest {\n"); - sb.append(" pullRequests: ").append(toIndentedString(pullRequests)).append("\n"); - sb.append(" rules: ").append(toIndentedString(rules)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces - * (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } - -} - diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequestWithBadPractices.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequestWithBadPractices.java deleted file mode 100644 index 7993f037..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequestWithBadPractices.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Hephaestus Intelligence Service API - * API documentation for the Hephaestus Intelligence Service. - * - * The version of the OpenAPI document: 0.0.1 - * Contact: felixtj.dietrich@tum.de - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -package de.tum.in.www1.hephaestus.intelligenceservice.model; - -import java.util.Objects; -import java.util.Arrays; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.annotation.JsonValue; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonTypeName; -import org.hibernate.validator.constraints.*; - -/** - * PullRequestWithBadPractices - */ -@JsonPropertyOrder({ - PullRequestWithBadPractices.JSON_PROPERTY_BAD_PRACTICE_IDS, - PullRequestWithBadPractices.JSON_PROPERTY_PULL_REQUEST_ID -}) -@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") -public class PullRequestWithBadPractices { - public static final String JSON_PROPERTY_BAD_PRACTICE_IDS = "bad_practice_ids"; - private List badPracticeIds = new ArrayList<>(); - - public static final String JSON_PROPERTY_PULL_REQUEST_ID = "pull_request_id"; - private String pullRequestId; - - public PullRequestWithBadPractices() { - } - - public PullRequestWithBadPractices badPracticeIds(List badPracticeIds) { - - this.badPracticeIds = badPracticeIds; - return this; - } - - public PullRequestWithBadPractices addBadPracticeIdsItem(String badPracticeIdsItem) { - if (this.badPracticeIds == null) { - this.badPracticeIds = new ArrayList<>(); - } - this.badPracticeIds.add(badPracticeIdsItem); - return this; - } - - /** - * Get badPracticeIds - * @return badPracticeIds - */ - @jakarta.annotation.Nonnull - @JsonProperty(JSON_PROPERTY_BAD_PRACTICE_IDS) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - - public List getBadPracticeIds() { - return badPracticeIds; - } - - - @JsonProperty(JSON_PROPERTY_BAD_PRACTICE_IDS) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setBadPracticeIds(List badPracticeIds) { - this.badPracticeIds = badPracticeIds; - } - - public PullRequestWithBadPractices pullRequestId(String pullRequestId) { - - this.pullRequestId = pullRequestId; - return this; - } - - /** - * Get pullRequestId - * @return pullRequestId - */ - @jakarta.annotation.Nonnull - @JsonProperty(JSON_PROPERTY_PULL_REQUEST_ID) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - - public String getPullRequestId() { - return pullRequestId; - } - - - @JsonProperty(JSON_PROPERTY_PULL_REQUEST_ID) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setPullRequestId(String pullRequestId) { - this.pullRequestId = pullRequestId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - PullRequestWithBadPractices pullRequestWithBadPractices = (PullRequestWithBadPractices) o; - return Objects.equals(this.badPracticeIds, pullRequestWithBadPractices.badPracticeIds) && - Objects.equals(this.pullRequestId, pullRequestWithBadPractices.pullRequestId); - } - - @Override - public int hashCode() { - return Objects.hash(badPracticeIds, pullRequestId); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class PullRequestWithBadPractices {\n"); - sb.append(" badPracticeIds: ").append(toIndentedString(badPracticeIds)).append("\n"); - sb.append(" pullRequestId: ").append(toIndentedString(pullRequestId)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces - * (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } - -} - diff --git a/server/intelligence-service/openapi.yaml b/server/intelligence-service/openapi.yaml index 30f186af..bad1fe2c 100644 --- a/server/intelligence-service/openapi.yaml +++ b/server/intelligence-service/openapi.yaml @@ -1,32 +1,33 @@ components: schemas: - DetectorRequest: + BadPractice: + description: A detected bad practice in a pull request. properties: - pull_requests: - items: - $ref: '#/components/schemas/PullRequest' - title: Pull Requests - type: array - rules: - items: - $ref: '#/components/schemas/Rule' - title: Rules - type: array + description: + description: The description of the bad practice. + title: Description + type: string + title: + description: The title of the bad practice. + title: Title + type: string required: - - pull_requests - - rules - title: DetectorRequest + - title + - description + title: BadPractice type: object - DetectorResponse: + BadPracticeList: + description: A list of bad practices detected in a pull request. properties: - detectBadPractices: + bad_practices: + description: A list of bad practices detected in a pull request. items: - $ref: '#/components/schemas/PullRequestWithBadPractices' - title: Detectbadpractices + $ref: '#/components/schemas/BadPractice' + title: Bad Practices type: array required: - - detectBadPractices - title: DetectorResponse + - bad_practices + title: BadPracticeList type: object HTTPValidationError: properties: @@ -103,38 +104,6 @@ components: - description title: PullRequest type: object - PullRequestWithBadPractices: - properties: - bad_practice_ids: - items: - type: string - title: Bad Practice Ids - type: array - pull_request_id: - title: Pull Request Id - type: string - required: - - pull_request_id - - bad_practice_ids - title: PullRequestWithBadPractices - type: object - Rule: - properties: - bad_practice_id: - title: Bad Practice Id - type: string - description: - title: Description - type: string - name: - title: Name - type: string - required: - - name - - description - - bad_practice_id - title: Rule - type: object ValidationError: properties: loc: @@ -172,14 +141,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DetectorRequest' + $ref: '#/components/schemas/PullRequest' required: true responses: '200': content: application/json: schema: - $ref: '#/components/schemas/DetectorResponse' + $ref: '#/components/schemas/BadPracticeList' description: Successful Response '422': content: From f478913513949658748f0e0fde899ca3faa91c58 Mon Sep 17 00:00:00 2001 From: Florian Ehrenstorfer Date: Tue, 28 Jan 2025 18:30:34 +0100 Subject: [PATCH 07/13] connect to llm #226 --- .../app/detector/bad_practice_detector.py | 8 +++++--- ...r => pullrequest_badpractice_detector.txt} | 4 ++++ .../app/routers/detector.py | 19 +++++++++++++++---- 3 files changed, 24 insertions(+), 7 deletions(-) rename server/intelligence-service/app/detector/prompts/{pullrequest_badpractice_detector => pullrequest_badpractice_detector.txt} (80%) diff --git a/server/intelligence-service/app/detector/bad_practice_detector.py b/server/intelligence-service/app/detector/bad_practice_detector.py index 40102d5c..c5501841 100644 --- a/server/intelligence-service/app/detector/bad_practice_detector.py +++ b/server/intelligence-service/app/detector/bad_practice_detector.py @@ -1,3 +1,4 @@ +from pathlib import Path from typing import List from langchain_core.prompts import PromptTemplate, ChatPromptTemplate @@ -23,11 +24,12 @@ class BadPracticeList(BaseModel): bad_practices: List[BadPractice] = Field(description="A list of bad practices detected in a pull request.") -def detectbadpractices(pull_requests: PullRequest) -> BadPracticeList: - with open("./prompts/pullrequest_badpractice_detector.txt", "r") as f: +def detectbadpractices(title, description) -> BadPracticeList: + prompt_path = Path(__file__).parent / "prompts" / "pullrequest_badpractice_detector.txt" + with open(prompt_path, "r", encoding="utf-8") as f: prompt_text = f.read() prompt_template = ChatPromptTemplate.from_template(prompt_text) - prompt = prompt_template.invoke(title=pull_requests.title, description=pull_requests.description) + prompt = prompt_template.invoke({"title": title, "description": description}) structured_llm = model.with_structured_output(BadPracticeList) response = structured_llm.invoke(prompt) return response \ No newline at end of file diff --git a/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector b/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.txt similarity index 80% rename from server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector rename to server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.txt index 1aa76e54..8a306e9c 100644 --- a/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector +++ b/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.txt @@ -23,6 +23,10 @@ GUIDELINES: REQUIREMENTS: 1. Identify and describe all bad practices in the pull request title and description. 2. Provide a clear title and a brief and concise description for each detected bad practice. +3. Return a list of all detected bad practices in the pull request. Return multiple bad practices if necessary. +4. Use clear and concise language to describe the bad practices. +5. Keep titles consistent between detections so that the same bad practice is identified in the same way. +6. Multiple runs on the same title and description should return the same results if nothing has changed. Pull Request Title: {title} Pull Request Description: {description} \ No newline at end of file diff --git a/server/intelligence-service/app/routers/detector.py b/server/intelligence-service/app/routers/detector.py index 9b2be4cf..7e5875a6 100644 --- a/server/intelligence-service/app/routers/detector.py +++ b/server/intelligence-service/app/routers/detector.py @@ -1,12 +1,23 @@ +from typing import List + from fastapi import APIRouter -from ..detector.bad_practice_detector import PullRequest, detectbadpractices, BadPracticeList +from openai import BaseModel + +from ..detector.bad_practice_detector import PullRequest, detectbadpractices, BadPracticeList, BadPractice router = APIRouter(prefix="/detector", tags=["detector"]) +class DetectorRequest(BaseModel): + title: str + description: str + +class DetectorResponse(BaseModel): + bad_practices: List[BadPractice] + @router.post( "/", - response_model=BadPracticeList, + response_model=DetectorResponse, summary="Detect bad practices given rules.", ) -def detect(request: PullRequest): - return detectbadpractices(request) +def detect(request: DetectorRequest): + return detectbadpractices(request.title, request.description) From 5c099b729dfc09a358562ddb07928ec584e97524 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 Jan 2025 17:35:24 +0000 Subject: [PATCH 08/13] chore: update API specs and client --- .../intelligenceservice/api/DetectorApi.java | 28 ++++----- ...{PullRequest.java => DetectorRequest.java} | 60 ++++++------------- ...racticeList.java => DetectorResponse.java} | 27 +++++---- server/intelligence-service/openapi.yaml | 42 ++++++------- 4 files changed, 66 insertions(+), 91 deletions(-) rename server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/{PullRequest.java => DetectorRequest.java} (71%) rename server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/{BadPracticeList.java => DetectorResponse.java} (78%) diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java index 7f3ccca8..7a6b081f 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DetectorApi.java @@ -3,9 +3,9 @@ import de.tum.in.www1.hephaestus.intelligenceservice.ApiClient; import de.tum.in.www1.hephaestus.intelligenceservice.BaseApi; -import de.tum.in.www1.hephaestus.intelligenceservice.model.BadPracticeList; +import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorRequest; +import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorResponse; import de.tum.in.www1.hephaestus.intelligenceservice.model.HTTPValidationError; -import de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequest; import java.util.Collections; import java.util.HashMap; @@ -44,12 +44,12 @@ public DetectorApi(ApiClient apiClient) { * *

200 - Successful Response *

422 - Validation Error - * @param pullRequest (required) - * @return BadPracticeList + * @param detectorRequest (required) + * @return DetectorResponse * @throws RestClientException if an error occurs while attempting to invoke the API */ - public BadPracticeList detectDetectorPost(PullRequest pullRequest) throws RestClientException { - return detectDetectorPostWithHttpInfo(pullRequest).getBody(); + public DetectorResponse detectDetectorPost(DetectorRequest detectorRequest) throws RestClientException { + return detectDetectorPostWithHttpInfo(detectorRequest).getBody(); } /** @@ -57,16 +57,16 @@ public BadPracticeList detectDetectorPost(PullRequest pullRequest) throws RestCl * *

200 - Successful Response *

422 - Validation Error - * @param pullRequest (required) - * @return ResponseEntity<BadPracticeList> + * @param detectorRequest (required) + * @return ResponseEntity<DetectorResponse> * @throws RestClientException if an error occurs while attempting to invoke the API */ - public ResponseEntity detectDetectorPostWithHttpInfo(PullRequest pullRequest) throws RestClientException { - Object localVarPostBody = pullRequest; + public ResponseEntity detectDetectorPostWithHttpInfo(DetectorRequest detectorRequest) throws RestClientException { + Object localVarPostBody = detectorRequest; - // verify the required parameter 'pullRequest' is set - if (pullRequest == null) { - throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'pullRequest' when calling detectDetectorPost"); + // verify the required parameter 'detectorRequest' is set + if (detectorRequest == null) { + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'detectorRequest' when calling detectDetectorPost"); } @@ -86,7 +86,7 @@ public ResponseEntity detectDetectorPostWithHttpInfo(PullReques String[] localVarAuthNames = new String[] { }; - ParameterizedTypeReference localReturnType = new ParameterizedTypeReference() {}; + ParameterizedTypeReference localReturnType = new ParameterizedTypeReference() {}; return apiClient.invokeAPI("/detector/", HttpMethod.POST, Collections.emptyMap(), localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType, localVarAuthNames, localReturnType); } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequest.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java similarity index 71% rename from server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequest.java rename to server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java index f00e3624..05c4d680 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/PullRequest.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java @@ -20,33 +20,32 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonValue; +import java.util.HashMap; +import java.util.Map; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonTypeName; import org.hibernate.validator.constraints.*; /** - * PullRequest + * DetectorRequest */ @JsonPropertyOrder({ - PullRequest.JSON_PROPERTY_DESCRIPTION, - PullRequest.JSON_PROPERTY_ID, - PullRequest.JSON_PROPERTY_TITLE + DetectorRequest.JSON_PROPERTY_DESCRIPTION, + DetectorRequest.JSON_PROPERTY_TITLE }) @jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") -public class PullRequest { +public class DetectorRequest extends HashMap { public static final String JSON_PROPERTY_DESCRIPTION = "description"; private String description; - public static final String JSON_PROPERTY_ID = "id"; - private String id; - public static final String JSON_PROPERTY_TITLE = "title"; private String title; - public PullRequest() { + public DetectorRequest() { + } - public PullRequest description(String description) { + public DetectorRequest description(String description) { this.description = description; return this; @@ -71,32 +70,7 @@ public void setDescription(String description) { this.description = description; } - public PullRequest id(String id) { - - this.id = id; - return this; - } - - /** - * Get id - * @return id - */ - @jakarta.annotation.Nonnull - @JsonProperty(JSON_PROPERTY_ID) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - - public String getId() { - return id; - } - - - @JsonProperty(JSON_PROPERTY_ID) - @JsonInclude(value = JsonInclude.Include.ALWAYS) - public void setId(String id) { - this.id = id; - } - - public PullRequest title(String title) { + public DetectorRequest title(String title) { this.title = title; return this; @@ -129,23 +103,23 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - PullRequest pullRequest = (PullRequest) o; - return Objects.equals(this.description, pullRequest.description) && - Objects.equals(this.id, pullRequest.id) && - Objects.equals(this.title, pullRequest.title); + DetectorRequest detectorRequest = (DetectorRequest) o; + return Objects.equals(this.description, detectorRequest.description) && + Objects.equals(this.title, detectorRequest.title) && + super.equals(o); } @Override public int hashCode() { - return Objects.hash(description, id, title); + return Objects.hash(description, title, super.hashCode()); } @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("class PullRequest {\n"); + sb.append("class DetectorRequest {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); sb.append(" description: ").append(toIndentedString(description)).append("\n"); - sb.append(" id: ").append(toIndentedString(id)).append("\n"); sb.append(" title: ").append(toIndentedString(title)).append("\n"); sb.append("}"); return sb.toString(); diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/BadPracticeList.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java similarity index 78% rename from server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/BadPracticeList.java rename to server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java index a55b7f33..1187bbcc 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/BadPracticeList.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java @@ -23,32 +23,35 @@ import de.tum.in.www1.hephaestus.intelligenceservice.model.BadPractice; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonTypeName; import org.hibernate.validator.constraints.*; /** - * A list of bad practices detected in a pull request. + * DetectorResponse */ @JsonPropertyOrder({ - BadPracticeList.JSON_PROPERTY_BAD_PRACTICES + DetectorResponse.JSON_PROPERTY_BAD_PRACTICES }) @jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") -public class BadPracticeList { +public class DetectorResponse extends HashMap { public static final String JSON_PROPERTY_BAD_PRACTICES = "bad_practices"; private List badPractices = new ArrayList<>(); - public BadPracticeList() { + public DetectorResponse() { + } - public BadPracticeList badPractices(List badPractices) { + public DetectorResponse badPractices(List badPractices) { this.badPractices = badPractices; return this; } - public BadPracticeList addBadPracticesItem(BadPractice badPracticesItem) { + public DetectorResponse addBadPracticesItem(BadPractice badPracticesItem) { if (this.badPractices == null) { this.badPractices = new ArrayList<>(); } @@ -57,7 +60,7 @@ public BadPracticeList addBadPracticesItem(BadPractice badPracticesItem) { } /** - * A list of bad practices detected in a pull request. + * Get badPractices * @return badPractices */ @jakarta.annotation.Nonnull @@ -83,19 +86,21 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - BadPracticeList badPracticeList = (BadPracticeList) o; - return Objects.equals(this.badPractices, badPracticeList.badPractices); + DetectorResponse detectorResponse = (DetectorResponse) o; + return Objects.equals(this.badPractices, detectorResponse.badPractices) && + super.equals(o); } @Override public int hashCode() { - return Objects.hash(badPractices); + return Objects.hash(badPractices, super.hashCode()); } @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("class BadPracticeList {\n"); + sb.append("class DetectorResponse {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); sb.append(" badPractices: ").append(toIndentedString(badPractices)).append("\n"); sb.append("}"); return sb.toString(); diff --git a/server/intelligence-service/openapi.yaml b/server/intelligence-service/openapi.yaml index bad1fe2c..ac10733a 100644 --- a/server/intelligence-service/openapi.yaml +++ b/server/intelligence-service/openapi.yaml @@ -16,18 +16,31 @@ components: - description title: BadPractice type: object - BadPracticeList: - description: A list of bad practices detected in a pull request. + DetectorRequest: + additionalProperties: true + properties: + description: + title: Description + type: string + title: + title: Title + type: string + required: + - title + - description + title: DetectorRequest + type: object + DetectorResponse: + additionalProperties: true properties: bad_practices: - description: A list of bad practices detected in a pull request. items: $ref: '#/components/schemas/BadPractice' title: Bad Practices type: array required: - bad_practices - title: BadPracticeList + title: DetectorResponse type: object HTTPValidationError: properties: @@ -87,23 +100,6 @@ components: - dev_progress title: MentorStartRequest type: object - PullRequest: - properties: - description: - title: Description - type: string - id: - title: Id - type: string - title: - title: Title - type: string - required: - - id - - title - - description - title: PullRequest - type: object ValidationError: properties: loc: @@ -141,14 +137,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PullRequest' + $ref: '#/components/schemas/DetectorRequest' required: true responses: '200': content: application/json: schema: - $ref: '#/components/schemas/BadPracticeList' + $ref: '#/components/schemas/DetectorResponse' description: Successful Response '422': content: From 8690f2ccb350eb6513543a4720ada91d8637df38 Mon Sep 17 00:00:00 2001 From: Florian Ehrenstorfer Date: Fri, 31 Jan 2025 10:51:14 +0100 Subject: [PATCH 09/13] connect bad practice detection(#223) --- .../hephaestus/activity/ActivityService.java | 24 +++-- .../PullRequestBadPracticeDetector.java | 90 +++++-------------- .../model/PullRequestBadPractice.java | 12 ++- .../config/BadPracticeDetectorConfig.java | 26 ++++++ .../pullrequest/PullRequestRepository.java | 2 + .../model/DetectorRequest.java | 8 +- .../model/DetectorResponse.java | 8 +- .../app/routers/detector.py | 2 +- server/intelligence-service/app/settings.py | 5 ++ .../activity/activity-dashboard.component.ts | 2 +- 10 files changed, 79 insertions(+), 100 deletions(-) create mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/BadPracticeDetectorConfig.java diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java index 41ebe795..00b3a4e8 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java @@ -7,6 +7,8 @@ import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestRepository; import de.tum.in.www1.hephaestus.gitprovider.user.UserRepository; import jakarta.transaction.Transactional; + +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; @@ -61,12 +63,7 @@ public ActivityDTO getActivity(String login) { .map(pullRequest -> PullRequestWithBadPracticesDTO.fromPullRequest( pullRequest, - pullRequestBadPracticesMap.getOrDefault( - pullRequest, - List.of( - new PullRequestBadPracticeDTO("Unchecked checkbox.", "The checkbox is not checked.", false) - ) - ) + pullRequestBadPracticesMap.getOrDefault(pullRequest, List.of()) ) ) .collect(Collectors.toList()); @@ -78,18 +75,17 @@ public ActivityDTO getActivity(String login) { public List detectBadPractices(String login) { logger.info("Detecting bad practices for user with login: {}", login); - userRepository.findByLogin(login).orElseThrow(() -> new IllegalArgumentException("User not found")); - List pullRequests = pullRequestRepository.findAssignedByLoginAndStates( login, Set.of(Issue.State.OPEN) ); - List badPractices = pullRequestBadPracticeDetector.detectAndSyncBadPractices(pullRequests); - - return badPractices - .stream() - .map(PullRequestBadPracticeDTO::fromPullRequestBadPractice) - .collect(Collectors.toList()); + List detectedBadPractices = new ArrayList<>(); + for (PullRequest pullRequest : pullRequests) { + detectedBadPractices.addAll(pullRequestBadPracticeDetector.detectAndSyncBadPractices(pullRequest)); + } + return detectedBadPractices.stream().map(PullRequestBadPracticeDTO::fromPullRequestBadPractice).toList(); } + + } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java index bd24079f..cc2c337a 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java @@ -2,17 +2,15 @@ import de.tum.in.www1.hephaestus.activity.PullRequestBadPracticeRepository; import de.tum.in.www1.hephaestus.activity.model.PullRequestBadPractice; +import de.tum.in.www1.hephaestus.config.BadPracticeDetectorConfig.BadPracticeDetectorService; import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest; import java.util.LinkedList; import java.util.List; -import java.util.stream.Collectors; -import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestRepository; -import de.tum.in.www1.hephaestus.intelligenceservice.api.DetectorApi; +import de.tum.in.www1.hephaestus.intelligenceservice.model.BadPractice; import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorRequest; import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorResponse; -import de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequestWithBadPractices; -import de.tum.in.www1.hephaestus.intelligenceservice.model.Rule; +import jakarta.transaction.Transactional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -23,84 +21,36 @@ public class PullRequestBadPracticeDetector { private static final Logger logger = LoggerFactory.getLogger(PullRequestBadPracticeDetector.class); - @Autowired - private PullRequestRepository pullRequestRepository; - @Autowired private PullRequestBadPracticeRepository pullRequestBadPracticeRepository; - private final DetectorApi detectorApi = new DetectorApi(); - - public List detectAndSyncBadPractices(List pullRequests) { - logger.info("Detecting bad practices for pull requests."); - + @Autowired + private BadPracticeDetectorService detectorApi; + public List detectAndSyncBadPractices(PullRequest pullRequest) { + logger.info("Detecting bad practices for pull request: {}", pullRequest.getId()); DetectorRequest detectorRequest = new DetectorRequest(); - detectorRequest.setPullRequests(mapToApiPullRequests(pullRequests)); + detectorRequest.setDescription(pullRequest.getBody()); + detectorRequest.setTitle(pullRequest.getTitle()); DetectorResponse detectorResponse = detectorApi.detectDetectorPost(detectorRequest); List detectedBadPractices = new LinkedList<>(); - detectorResponse.getDetectBadPractices().forEach(prWithBadPractices -> - detectedBadPractices.addAll(handleDetectedBadPractices(prWithBadPractices)) - ); - - return detectedBadPractices; - } - - private List handleDetectedBadPractices(PullRequestWithBadPractices prWithBadPractices) { - - Long pullRequestId = Long.valueOf(prWithBadPractices.getPullRequestId()); - PullRequest pullRequest = pullRequestRepository.findById(pullRequestId).orElse(null); - - if (pullRequest == null) { - logger.error("Pull request with id {} not found.", prWithBadPractices.getPullRequestId()); - return List.of(); + for (BadPractice badPractice : detectorResponse.getBadPractices()) { + detectedBadPractices.add(handleDetectedBadPractices(pullRequest, badPractice)); } - List existingBadPractices = pullRequestBadPracticeRepository.findByPullRequestId( - pullRequest.getId() - ); - - List newBadPractices = prWithBadPractices.getBadPracticeIds().stream() - .map(badPracticeRule -> { - PullRequestBadPractice pullRequestBadPractice = new PullRequestBadPractice(); - pullRequestBadPractice.setPullrequest(pullRequest); - pullRequestBadPractice.setResolved(false); - return pullRequestBadPractice; - }) - .collect(Collectors.toList()); - - pullRequestBadPracticeRepository.saveAll(newBadPractices); - existingBadPractices.removeAll(newBadPractices); - existingBadPractices.forEach(badPractice -> badPractice.setResolved(true)); - pullRequestBadPracticeRepository.saveAll(existingBadPractices); - return newBadPractices; - } - - private BadPracticeType findBadPracticeType(String badPracticeRuleId) { - return pullRequestBadPracticeRuleRepository.findById(Long.valueOf(badPracticeRuleId)) - .map(PullRequestBadPracticeRule::getType) - .orElse(null); + logger.info("Detected {} bad practices for pull request: {}", detectedBadPractices.size(), pullRequest.getId()); + return detectedBadPractices; } - private List mapToApiPullRequests(List pullRequests) { - return pullRequests.stream().map(pullRequest -> { - de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequest apiPullRequest = new de.tum.in.www1.hephaestus.intelligenceservice.model.PullRequest(); - apiPullRequest.setId(String.valueOf(pullRequest.getId())); - apiPullRequest.setTitle(pullRequest.getTitle()); - apiPullRequest.setDescription(pullRequest.getBody()); - return apiPullRequest; - }).collect(Collectors.toList()); - } + private PullRequestBadPractice handleDetectedBadPractices(PullRequest pullRequest, BadPractice badPractice) { - private List mapToApiRules(List rules) { - return rules.stream().map(rule -> { - Rule apiRule = new Rule(); - apiRule.setBadPracticeId(String.valueOf(rule.getId())); - apiRule.setName(rule.getType().getTitle()); - apiRule.setDescription(rule.getType().getLlmPrompt()); - return apiRule; - }).collect(Collectors.toList()); + PullRequestBadPractice pullRequestBadPractice = new PullRequestBadPractice(); + pullRequestBadPractice.setTitle(badPractice.getTitle()); + pullRequestBadPractice.setDescription(badPractice.getDescription()); + pullRequestBadPractice.setPullrequest(pullRequest); + pullRequestBadPractice.setResolved(false); + return pullRequestBadPracticeRepository.save(pullRequestBadPractice); } } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java index 5f895984..e1a4303d 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java @@ -1,14 +1,14 @@ package de.tum.in.www1.hephaestus.activity.model; import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest; + import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; +import lombok.*; @Entity @Getter @@ -18,12 +18,16 @@ public class PullRequestBadPractice { @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @NonNull private String title; + @NonNull private String description; + @NonNull @ManyToOne @JoinColumn(name = "pullrequest_id") private PullRequest pullrequest; diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/BadPracticeDetectorConfig.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/BadPracticeDetectorConfig.java new file mode 100644 index 00000000..e8e33ccc --- /dev/null +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/BadPracticeDetectorConfig.java @@ -0,0 +1,26 @@ +package de.tum.in.www1.hephaestus.config; + +import de.tum.in.www1.hephaestus.intelligenceservice.ApiClient; +import de.tum.in.www1.hephaestus.intelligenceservice.api.DetectorApi; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class BadPracticeDetectorConfig { + + @Value("${hephaestus.intelligence-service.url}") + private String badPracticeDetectorUrl; + + @Bean + public BadPracticeDetectorService badPracticeDetectorService() { + return new BadPracticeDetectorService(); + } + + public class BadPracticeDetectorService extends DetectorApi { + + public BadPracticeDetectorService() { + super(new ApiClient().setBasePath(badPracticeDetectorUrl)); + } + } +} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java index 7131365a..ed22e954 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java @@ -4,6 +4,8 @@ import java.util.List; import java.util.Optional; import java.util.Set; + +import jakarta.transaction.Transactional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java index 05c4d680..d859f9f4 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorRequest.java @@ -34,7 +34,7 @@ DetectorRequest.JSON_PROPERTY_TITLE }) @jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") -public class DetectorRequest extends HashMap { +public class DetectorRequest { public static final String JSON_PROPERTY_DESCRIPTION = "description"; private String description; @@ -105,20 +105,18 @@ public boolean equals(Object o) { } DetectorRequest detectorRequest = (DetectorRequest) o; return Objects.equals(this.description, detectorRequest.description) && - Objects.equals(this.title, detectorRequest.title) && - super.equals(o); + Objects.equals(this.title, detectorRequest.title); } @Override public int hashCode() { - return Objects.hash(description, title, super.hashCode()); + return Objects.hash(description, title); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class DetectorRequest {\n"); - sb.append(" ").append(toIndentedString(super.toString())).append("\n"); sb.append(" description: ").append(toIndentedString(description)).append("\n"); sb.append(" title: ").append(toIndentedString(title)).append("\n"); sb.append("}"); diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java index 1187bbcc..adbd004a 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/DetectorResponse.java @@ -37,7 +37,7 @@ DetectorResponse.JSON_PROPERTY_BAD_PRACTICES }) @jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") -public class DetectorResponse extends HashMap { +public class DetectorResponse { public static final String JSON_PROPERTY_BAD_PRACTICES = "bad_practices"; private List badPractices = new ArrayList<>(); @@ -87,20 +87,18 @@ public boolean equals(Object o) { return false; } DetectorResponse detectorResponse = (DetectorResponse) o; - return Objects.equals(this.badPractices, detectorResponse.badPractices) && - super.equals(o); + return Objects.equals(this.badPractices, detectorResponse.badPractices); } @Override public int hashCode() { - return Objects.hash(badPractices, super.hashCode()); + return Objects.hash(badPractices); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class DetectorResponse {\n"); - sb.append(" ").append(toIndentedString(super.toString())).append("\n"); sb.append(" badPractices: ").append(toIndentedString(badPractices)).append("\n"); sb.append("}"); return sb.toString(); diff --git a/server/intelligence-service/app/routers/detector.py b/server/intelligence-service/app/routers/detector.py index 7e5875a6..6143505c 100644 --- a/server/intelligence-service/app/routers/detector.py +++ b/server/intelligence-service/app/routers/detector.py @@ -17,7 +17,7 @@ class DetectorResponse(BaseModel): @router.post( "/", response_model=DetectorResponse, - summary="Detect bad practices given rules.", + summary="Detect bad practices for given pull request.", ) def detect(request: DetectorRequest): return detectbadpractices(request.title, request.description) diff --git a/server/intelligence-service/app/settings.py b/server/intelligence-service/app/settings.py index a01f8bc5..cf074a55 100644 --- a/server/intelligence-service/app/settings.py +++ b/server/intelligence-service/app/settings.py @@ -17,6 +17,11 @@ class Settings(BaseSettings): AZURE_OPENAI_ENDPOINT: str = "" AZURE_OPENAI_API_VERSION: str = "" + LANGSMITH_TRACING: bool = True + LANGSMITH_ENDPOINT: str = "" + LANGSMITH_API_KEY: str = "" + LANGSMITH_PROJECT: str = "" + MODEL_NAME: str = "gpt-4o" MODEL_TEMPERATURE: float = 0.7 MODEL_MAX_TOKENS: int = 4096 diff --git a/webapp/src/app/home/activity/activity-dashboard.component.ts b/webapp/src/app/home/activity/activity-dashboard.component.ts index 5ac2bde0..5a30b42c 100644 --- a/webapp/src/app/home/activity/activity-dashboard.component.ts +++ b/webapp/src/app/home/activity/activity-dashboard.component.ts @@ -33,7 +33,7 @@ export class ActivityDashboardComponent { detectBadPractices = () => { console.log('Detecting bad practices'); - //this.activityService.detectBadPractices(this.userLogin!).subscribe(); + this.activityService.detectBadPracticesByUser(this.userLogin!).subscribe(); }; protected readonly RefreshCcw = RefreshCcw; } From 9660567942a282140d9cef61992cf20ec17a2bf0 Mon Sep 17 00:00:00 2001 From: Florian Ehrenstorfer Date: Fri, 31 Jan 2025 17:59:02 +0100 Subject: [PATCH 10/13] adjust changelog(#223) --- .../tum/in/www1/hephaestus/activity/ActivityService.java | 9 +++++---- .../PullRequestBadPracticeDetector.java | 3 ++- .../activity/model/PullRequestBadPractice.java | 8 ++------ .../gitprovider/pullrequest/PullRequestRepository.java | 1 + .../resources/db/changelog/1738063112622_changelog.xml | 6 +++--- .../app/home/activity/activity-dashboard.component.ts | 2 +- .../pull-request-bad-practice-card.component.html | 2 +- 7 files changed, 15 insertions(+), 16 deletions(-) diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java index 00b3a4e8..16f366ec 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java @@ -48,7 +48,7 @@ public ActivityDTO getActivity(String login) { pullRequestBadPracticeRepository.findAssignedByLoginAndOpen(login); Map> pullRequestBadPracticesMap = openPulLRequestBadPractices - .stream() + .stream().filter(pullRequestBadPractice -> !pullRequestBadPractice.isResolved()) .collect( Collectors.groupingBy( PullRequestBadPractice::getPullrequest, @@ -71,7 +71,6 @@ public ActivityDTO getActivity(String login) { return new ActivityDTO(openPullRequestsWithBadPractices); } - @Transactional public List detectBadPractices(String login) { logger.info("Detecting bad practices for user with login: {}", login); @@ -80,12 +79,14 @@ public List detectBadPractices(String login) { Set.of(Issue.State.OPEN) ); + List existingBadPractices = pullRequestBadPracticeRepository.findAssignedByLoginAndOpen(login); + existingBadPractices.forEach(existingBadPractice -> existingBadPractice.setResolved(true)); + pullRequestBadPracticeRepository.saveAll(existingBadPractices); + List detectedBadPractices = new ArrayList<>(); for (PullRequest pullRequest : pullRequests) { detectedBadPractices.addAll(pullRequestBadPracticeDetector.detectAndSyncBadPractices(pullRequest)); } return detectedBadPractices.stream().map(PullRequestBadPracticeDTO::fromPullRequestBadPractice).toList(); } - - } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java index cc2c337a..e9d8d863 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java @@ -44,7 +44,8 @@ public List detectAndSyncBadPractices(PullRequest pullRe return detectedBadPractices; } - private PullRequestBadPractice handleDetectedBadPractices(PullRequest pullRequest, BadPractice badPractice) { + @Transactional + protected PullRequestBadPractice handleDetectedBadPractices(PullRequest pullRequest, BadPractice badPractice) { PullRequestBadPractice pullRequestBadPractice = new PullRequestBadPractice(); pullRequestBadPractice.setTitle(badPractice.getTitle()); diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java index e1a4303d..040f99ab 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java @@ -2,12 +2,7 @@ import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import jakarta.persistence.*; import lombok.*; @Entity @@ -15,6 +10,7 @@ @Setter @NoArgsConstructor @ToString +@Table(name="pullrequestbadpractice") public class PullRequestBadPractice { @Id diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java index ed22e954..c49fb365 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java @@ -22,6 +22,7 @@ SELECT MIN(p.createdAt) ) Optional firstContributionByAuthorLogin(@Param("authorLogin") String authorLogin); + @Transactional @Query( """ SELECT p diff --git a/server/application-server/src/main/resources/db/changelog/1738063112622_changelog.xml b/server/application-server/src/main/resources/db/changelog/1738063112622_changelog.xml index 237c8ec6..5b05e81f 100644 --- a/server/application-server/src/main/resources/db/changelog/1738063112622_changelog.xml +++ b/server/application-server/src/main/resources/db/changelog/1738063112622_changelog.xml @@ -1,8 +1,8 @@ - - + + @@ -14,6 +14,6 @@ - + diff --git a/webapp/src/app/home/activity/activity-dashboard.component.ts b/webapp/src/app/home/activity/activity-dashboard.component.ts index 5a30b42c..bbb23eef 100644 --- a/webapp/src/app/home/activity/activity-dashboard.component.ts +++ b/webapp/src/app/home/activity/activity-dashboard.component.ts @@ -20,6 +20,7 @@ export class ActivityDashboardComponent { protected userLogin: string | null = null; protected numberOfPullRequests = computed(() => this.query.data()?.pullRequests?.length ?? 0); protected numberOfBadPractices = computed(() => this.query.data()?.pullRequests?.reduce((acc, pr) => acc + (pr.badPractices?.length ?? 0), 0) ?? 0); + protected readonly RefreshCcw = RefreshCcw; constructor(private route: ActivatedRoute) { this.userLogin = this.route.snapshot.paramMap.get('id'); @@ -35,5 +36,4 @@ export class ActivityDashboardComponent { console.log('Detecting bad practices'); this.activityService.detectBadPracticesByUser(this.userLogin!).subscribe(); }; - protected readonly RefreshCcw = RefreshCcw; } diff --git a/webapp/src/app/user/pull-request-bad-practice-card/pull-request-bad-practice-card.component.html b/webapp/src/app/user/pull-request-bad-practice-card/pull-request-bad-practice-card.component.html index 532914e3..8b23af05 100644 --- a/webapp/src/app/user/pull-request-bad-practice-card/pull-request-bad-practice-card.component.html +++ b/webapp/src/app/user/pull-request-bad-practice-card/pull-request-bad-practice-card.component.html @@ -58,7 +58,7 @@ @for (badpractice of badPractices(); track badpractice.title) { - + } From 0951d432716206cda11b5cca4877f09206693d34 Mon Sep 17 00:00:00 2001 From: Florian Ehrenstorfer Date: Thu, 20 Feb 2025 16:50:14 +0100 Subject: [PATCH 11/13] rework after review #232 --- .../PullRequestBadPracticeDetector.java | 8 ++++-- .../model/PullRequestBadPractice.java | 8 +++++- .../config/BadPracticeDetectorConfig.java | 26 ------------------- .../config/IntelligenceServiceConfig.java | 13 ++++++++++ .../pullrequest/PullRequestRepository.java | 1 - .../app/detector/bad_practice_detector.py | 7 +++-- ...xt => pullrequest_badpractice_detector.py} | 3 ++- 7 files changed, 31 insertions(+), 35 deletions(-) delete mode 100644 server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/BadPracticeDetectorConfig.java rename server/intelligence-service/app/detector/prompts/{pullrequest_badpractice_detector.txt => pullrequest_badpractice_detector.py} (96%) diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java index e9d8d863..6fff1687 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java @@ -2,7 +2,7 @@ import de.tum.in.www1.hephaestus.activity.PullRequestBadPracticeRepository; import de.tum.in.www1.hephaestus.activity.model.PullRequestBadPractice; -import de.tum.in.www1.hephaestus.config.BadPracticeDetectorConfig.BadPracticeDetectorService; +import de.tum.in.www1.hephaestus.config.IntelligenceServiceConfig.BadPracticeDetectorService; import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest; import java.util.LinkedList; import java.util.List; @@ -27,6 +27,11 @@ public class PullRequestBadPracticeDetector { @Autowired private BadPracticeDetectorService detectorApi; + /** + * Detects bad practices in the given pull request and syncs them with the database. + * @param pullRequest The pull request to detect bad practices in. + * @return The detected bad practices. + */ public List detectAndSyncBadPractices(PullRequest pullRequest) { logger.info("Detecting bad practices for pull request: {}", pullRequest.getId()); DetectorRequest detectorRequest = new DetectorRequest(); @@ -44,7 +49,6 @@ public List detectAndSyncBadPractices(PullRequest pullRe return detectedBadPractices; } - @Transactional protected PullRequestBadPractice handleDetectedBadPractices(PullRequest pullRequest, BadPractice badPractice) { PullRequestBadPractice pullRequestBadPractice = new PullRequestBadPractice(); diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java index 040f99ab..abad8608 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java @@ -2,7 +2,13 @@ import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest; -import jakarta.persistence.*; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Table; import lombok.*; @Entity diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/BadPracticeDetectorConfig.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/BadPracticeDetectorConfig.java deleted file mode 100644 index e8e33ccc..00000000 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/BadPracticeDetectorConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.tum.in.www1.hephaestus.config; - -import de.tum.in.www1.hephaestus.intelligenceservice.ApiClient; -import de.tum.in.www1.hephaestus.intelligenceservice.api.DetectorApi; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class BadPracticeDetectorConfig { - - @Value("${hephaestus.intelligence-service.url}") - private String badPracticeDetectorUrl; - - @Bean - public BadPracticeDetectorService badPracticeDetectorService() { - return new BadPracticeDetectorService(); - } - - public class BadPracticeDetectorService extends DetectorApi { - - public BadPracticeDetectorService() { - super(new ApiClient().setBasePath(badPracticeDetectorUrl)); - } - } -} diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/IntelligenceServiceConfig.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/IntelligenceServiceConfig.java index 327f4e3c..0ac693b1 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/IntelligenceServiceConfig.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/config/IntelligenceServiceConfig.java @@ -1,6 +1,7 @@ package de.tum.in.www1.hephaestus.config; import de.tum.in.www1.hephaestus.intelligenceservice.ApiClient; +import de.tum.in.www1.hephaestus.intelligenceservice.api.DetectorApi; import de.tum.in.www1.hephaestus.intelligenceservice.api.MentorApi; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -23,4 +24,16 @@ public IntelligenceServiceApi() { super(new ApiClient().setBasePath(intelligenceServiceUrl)); } } + + @Bean + public BadPracticeDetectorService badPracticeDetectorService() { + return new BadPracticeDetectorService(); + } + + public class BadPracticeDetectorService extends DetectorApi { + + public BadPracticeDetectorService() { + super(new ApiClient().setBasePath(intelligenceServiceUrl)); + } + } } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java index c49fb365..ed22e954 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java @@ -22,7 +22,6 @@ SELECT MIN(p.createdAt) ) Optional firstContributionByAuthorLogin(@Param("authorLogin") String authorLogin); - @Transactional @Query( """ SELECT p diff --git a/server/intelligence-service/app/detector/bad_practice_detector.py b/server/intelligence-service/app/detector/bad_practice_detector.py index c5501841..f5a90161 100644 --- a/server/intelligence-service/app/detector/bad_practice_detector.py +++ b/server/intelligence-service/app/detector/bad_practice_detector.py @@ -4,6 +4,7 @@ from langchain_core.prompts import PromptTemplate, ChatPromptTemplate from pydantic import BaseModel, Field +from .prompts.pullrequest_badpractice_detector import BAD_PRACTICE_PROMPT_TEST from ..model import model @@ -24,10 +25,8 @@ class BadPracticeList(BaseModel): bad_practices: List[BadPractice] = Field(description="A list of bad practices detected in a pull request.") -def detectbadpractices(title, description) -> BadPracticeList: - prompt_path = Path(__file__).parent / "prompts" / "pullrequest_badpractice_detector.txt" - with open(prompt_path, "r", encoding="utf-8") as f: - prompt_text = f.read() +def detect_bad_practices(title, description) -> BadPracticeList: + prompt_text = BAD_PRACTICE_PROMPT_TEST prompt_template = ChatPromptTemplate.from_template(prompt_text) prompt = prompt_template.invoke({"title": title, "description": description}) structured_llm = model.with_structured_output(BadPracticeList) diff --git a/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.txt b/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.py similarity index 96% rename from server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.txt rename to server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.py index 8a306e9c..b4863c2f 100644 --- a/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.txt +++ b/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.py @@ -1,3 +1,4 @@ +BAD_PRACTICE_PROMPT_TEST = """ You are a bad practice detector reviewing pull requests for bad practices. You analyze and review the title and description of the pull request to identify any bad practices. You detect bad practices based on guidelines for good pull request titles and descriptions. @@ -29,4 +30,4 @@ 6. Multiple runs on the same title and description should return the same results if nothing has changed. Pull Request Title: {title} -Pull Request Description: {description} \ No newline at end of file +Pull Request Description: {description}""" \ No newline at end of file From c2a20a9ac3fc3b29ba7a14452766494af8972184 Mon Sep 17 00:00:00 2001 From: Florian Ehrenstorfer Date: Thu, 20 Feb 2025 16:55:06 +0100 Subject: [PATCH 12/13] reformatting #226 --- .../hephaestus/activity/ActivityService.java | 8 +++++--- .../PullRequestBadPracticeDetector.java | 6 ++---- .../activity/model/PullRequestBadPractice.java | 9 ++++----- .../pullrequest/PullRequestRepository.java | 3 +-- .../app/detector/bad_practice_detector.py | 18 +++++++++++------- .../pullrequest_badpractice_detector.py | 2 +- .../app/routers/detector.py | 10 +++++++++- 7 files changed, 33 insertions(+), 23 deletions(-) diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java index 16f366ec..38ad50ee 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/ActivityService.java @@ -7,7 +7,6 @@ import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestRepository; import de.tum.in.www1.hephaestus.gitprovider.user.UserRepository; import jakarta.transaction.Transactional; - import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -48,7 +47,8 @@ public ActivityDTO getActivity(String login) { pullRequestBadPracticeRepository.findAssignedByLoginAndOpen(login); Map> pullRequestBadPracticesMap = openPulLRequestBadPractices - .stream().filter(pullRequestBadPractice -> !pullRequestBadPractice.isResolved()) + .stream() + .filter(pullRequestBadPractice -> !pullRequestBadPractice.isResolved()) .collect( Collectors.groupingBy( PullRequestBadPractice::getPullrequest, @@ -79,7 +79,9 @@ public List detectBadPractices(String login) { Set.of(Issue.State.OPEN) ); - List existingBadPractices = pullRequestBadPracticeRepository.findAssignedByLoginAndOpen(login); + List existingBadPractices = pullRequestBadPracticeRepository.findAssignedByLoginAndOpen( + login + ); existingBadPractices.forEach(existingBadPractice -> existingBadPractice.setResolved(true)); pullRequestBadPracticeRepository.saveAll(existingBadPractices); diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java index 6fff1687..ee11e27f 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/badpracticedetector/PullRequestBadPracticeDetector.java @@ -4,13 +4,12 @@ import de.tum.in.www1.hephaestus.activity.model.PullRequestBadPractice; import de.tum.in.www1.hephaestus.config.IntelligenceServiceConfig.BadPracticeDetectorService; import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest; -import java.util.LinkedList; -import java.util.List; - import de.tum.in.www1.hephaestus.intelligenceservice.model.BadPractice; import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorRequest; import de.tum.in.www1.hephaestus.intelligenceservice.model.DetectorResponse; import jakarta.transaction.Transactional; +import java.util.LinkedList; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -50,7 +49,6 @@ public List detectAndSyncBadPractices(PullRequest pullRe } protected PullRequestBadPractice handleDetectedBadPractices(PullRequest pullRequest, BadPractice badPractice) { - PullRequestBadPractice pullRequestBadPractice = new PullRequestBadPractice(); pullRequestBadPractice.setTitle(badPractice.getTitle()); pullRequestBadPractice.setDescription(badPractice.getDescription()); diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java index abad8608..c9c5abe2 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/activity/model/PullRequestBadPractice.java @@ -1,13 +1,12 @@ package de.tum.in.www1.hephaestus.activity.model; import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest; - -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.*; @@ -16,7 +15,7 @@ @Setter @NoArgsConstructor @ToString -@Table(name="pullrequestbadpractice") +@Table(name = "pullrequestbadpractice") public class PullRequestBadPractice { @Id diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java index ed22e954..44549b36 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java @@ -1,11 +1,10 @@ package de.tum.in.www1.hephaestus.gitprovider.pullrequest; +import jakarta.transaction.Transactional; import java.time.OffsetDateTime; import java.util.List; import java.util.Optional; import java.util.Set; - -import jakarta.transaction.Transactional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; diff --git a/server/intelligence-service/app/detector/bad_practice_detector.py b/server/intelligence-service/app/detector/bad_practice_detector.py index f5a90161..446633ed 100644 --- a/server/intelligence-service/app/detector/bad_practice_detector.py +++ b/server/intelligence-service/app/detector/bad_practice_detector.py @@ -13,22 +13,26 @@ class PullRequest(BaseModel): title: str description: str + class BadPractice(BaseModel): """A detected bad practice in a pull request.""" title: str = Field(description="The title of the bad practice.") description: str = Field(description="The description of the bad practice.") + class BadPracticeList(BaseModel): """A list of bad practices detected in a pull request.""" - bad_practices: List[BadPractice] = Field(description="A list of bad practices detected in a pull request.") + bad_practices: List[BadPractice] = Field( + description="A list of bad practices detected in a pull request." + ) def detect_bad_practices(title, description) -> BadPracticeList: - prompt_text = BAD_PRACTICE_PROMPT_TEST - prompt_template = ChatPromptTemplate.from_template(prompt_text) - prompt = prompt_template.invoke({"title": title, "description": description}) - structured_llm = model.with_structured_output(BadPracticeList) - response = structured_llm.invoke(prompt) - return response \ No newline at end of file + prompt_text = BAD_PRACTICE_PROMPT_TEST + prompt_template = ChatPromptTemplate.from_template(prompt_text) + prompt = prompt_template.invoke({"title": title, "description": description}) + structured_llm = model.with_structured_output(BadPracticeList) + response = structured_llm.invoke(prompt) + return response diff --git a/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.py b/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.py index b4863c2f..cc8de25f 100644 --- a/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.py +++ b/server/intelligence-service/app/detector/prompts/pullrequest_badpractice_detector.py @@ -30,4 +30,4 @@ 6. Multiple runs on the same title and description should return the same results if nothing has changed. Pull Request Title: {title} -Pull Request Description: {description}""" \ No newline at end of file +Pull Request Description: {description}""" diff --git a/server/intelligence-service/app/routers/detector.py b/server/intelligence-service/app/routers/detector.py index 6143505c..fa7856c7 100644 --- a/server/intelligence-service/app/routers/detector.py +++ b/server/intelligence-service/app/routers/detector.py @@ -3,17 +3,25 @@ from fastapi import APIRouter from openai import BaseModel -from ..detector.bad_practice_detector import PullRequest, detectbadpractices, BadPracticeList, BadPractice +from ..detector.bad_practice_detector import ( + PullRequest, + detect_bad_practices, + BadPracticeList, + BadPractice, +) router = APIRouter(prefix="/detector", tags=["detector"]) + class DetectorRequest(BaseModel): title: str description: str + class DetectorResponse(BaseModel): bad_practices: List[BadPractice] + @router.post( "/", response_model=DetectorResponse, From e06460de02872d3f98add4623fa375fa6a138810 Mon Sep 17 00:00:00 2001 From: Florian Ehrenstorfer Date: Thu, 20 Feb 2025 16:55:34 +0100 Subject: [PATCH 13/13] webapp reformatting #226 --- webapp/src/app/app.routes.ts | 1 - .../pull-request-bad-practice-card.component.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/webapp/src/app/app.routes.ts b/webapp/src/app/app.routes.ts index 7d097329..a497f933 100644 --- a/webapp/src/app/app.routes.ts +++ b/webapp/src/app/app.routes.ts @@ -49,5 +49,4 @@ export const routes: Routes = [ { path: 'mentor', component: MentorComponent, canActivate: [AuthGuard, MentorGuard] }, { path: 'workspace', component: WorkspaceComponent, canActivate: [AuthGuard, AdminGuard] }, { path: 'user/:id/activity', component: ActivityDashboardComponent, canActivate: [AuthGuard, AdminGuard] } - ]; diff --git a/webapp/src/app/user/pull-request-bad-practice-card/pull-request-bad-practice-card.component.ts b/webapp/src/app/user/pull-request-bad-practice-card/pull-request-bad-practice-card.component.ts index 564ce852..fa6dbc24 100644 --- a/webapp/src/app/user/pull-request-bad-practice-card/pull-request-bad-practice-card.component.ts +++ b/webapp/src/app/user/pull-request-bad-practice-card/pull-request-bad-practice-card.component.ts @@ -30,7 +30,7 @@ import { formatTitle } from '@app/utils'; BrnCollapsibleTriggerDirective, HlmButtonDirective, GithubLabelComponent - ], + ] }) export class PullRequestBadPracticeCardComponent { protected readonly octCheck = octCheck;