diff --git a/.github/workflows/pr_review.yml b/.github/workflows/pr_review.yml new file mode 100644 index 00000000..6b47d545 --- /dev/null +++ b/.github/workflows/pr_review.yml @@ -0,0 +1,44 @@ +name: PR Review Automation + +on: + pull_request: + types: [opened, synchronize] + +jobs: + review-pr: + runs-on: ubuntu-latest # Change to macos-latest if using Mac + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Install Dependencies + run: | + sudo apt update + sudo apt install -y jq curl + + - name: Install Ollama + run: | + curl -fsSL https://ollama.com/install.sh | bash + nohup ollama serve > /dev/null 2>&1 & # Run Ollama in background + + - name: Fetch PR Changes + run: | + CHANGED_FILES=$(git diff --name-only origin/main...HEAD) + echo "Changed Files:" + echo "$CHANGED_FILES" + + - name: Run PR Review with Ollama + run: | + REVIEW_TEXT="" + for FILE in $CHANGED_FILES; do + CODE=$(cat "$FILE") + REVIEW_TEXT+="Review for $FILE:\n" + REVIEW_TEXT+="$(echo "$CODE" | ollama run pr-reviewer)\n\n" + done + echo -e "$REVIEW_TEXT" > pr_review.txt + + - name: Post PR Review Comments + uses: thollander/actions-comment-pull-request@v2 + with: + message: "$(cat pr_review.txt)" diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..721cb38b --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + java + id("org.springframework.boot") version "3.4.1" + id("io.spring.dependency-management") version "1.1.7" +} + +group = "com.example" +version = "0.0.1-SNAPSHOT" + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +configurations { + compileOnly { + extendsFrom(configurations.annotationProcessor.get()) + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("com.github.javaparser:javaparser-core:3.25.2") + implementation("org.apache.opennlp:opennlp-tools:2.3.2") + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.postgresql:postgresql:42.6.0") + compileOnly("org.projectlombok:lombok") + annotationProcessor("org.projectlombok:lombok") + testImplementation("org.springframework.boot:spring-boot-starter-test") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +tasks.withType { + useJUnitPlatform() +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..a4b76b95 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..e2847c82 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..b23cdd42 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "rating" diff --git a/src/main/java/com/example/rating/Analyzer.java b/src/main/java/com/example/rating/Analyzer.java new file mode 100644 index 00000000..c742b27c --- /dev/null +++ b/src/main/java/com/example/rating/Analyzer.java @@ -0,0 +1,20 @@ +package com.example.rating; + +import com.github.javaparser.JavaParser; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.body.MethodDeclaration; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class Analyzer { + public static void main(String[] args) throws Exception { + String code = new String(Files.readAllBytes(Paths.get("/Users/in45828930/Downloads/rating/src/main/java/com/example/rating/RatingService.java"))); + CompilationUnit cu = new JavaParser().parse(code).getResult().orElseThrow(); + + cu.findAll(MethodDeclaration.class).forEach(method -> { + System.out.println("Method Name: " + method.getName()); + }); + } +} + diff --git a/src/main/java/com/example/rating/OpenNlpExample.java b/src/main/java/com/example/rating/OpenNlpExample.java new file mode 100644 index 00000000..305eb2ce --- /dev/null +++ b/src/main/java/com/example/rating/OpenNlpExample.java @@ -0,0 +1,31 @@ +package com.example.rating; + +import opennlp.tools.sentdetect.SentenceDetectorME; +import opennlp.tools.sentdetect.SentenceModel; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; + +public class OpenNlpExample { + public static void main(String[] args) { + try (InputStream modelIn = new FileInputStream(new File("/Users/in45828930/Downloads/rating/src/main/java/com/example/rating/da-sent.bin"))) { + SentenceModel model = new SentenceModel(modelIn); + SentenceDetectorME sentenceDetector = new SentenceDetectorME(model); + + String javaComment = " CompletableFuture> responseFuture = CompletableFuture.supplyAsync(() -> {\n" + + " try {\n" + + " return restTemplate.getForEntity(FEED_URL, String.class);\n" + + " } catch (Exception e) {\n" + + " throw new RuntimeException(\"Failed to fetch data\", e);\n" + + " }\n" + + " });"; + String[] sentences = sentenceDetector.sentDetect(javaComment); + + for (String sentence : sentences) { + System.out.println(sentence); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/example/rating/Rating.java b/src/main/java/com/example/rating/Rating.java new file mode 100644 index 00000000..cdaa5ae0 --- /dev/null +++ b/src/main/java/com/example/rating/Rating.java @@ -0,0 +1,29 @@ +package com.example.rating; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.util.UUID; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Rating { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private UUID recipeId; + + private String urlFriendlyTitle; + + + private String averageRating; + + private LocalDate currentDay; +} diff --git a/src/main/java/com/example/rating/RatingApplication.java b/src/main/java/com/example/rating/RatingApplication.java new file mode 100644 index 00000000..041c66b7 --- /dev/null +++ b/src/main/java/com/example/rating/RatingApplication.java @@ -0,0 +1,13 @@ +package com.example.rating; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class RatingApplication { + + public static void main(String[] args) { + SpringApplication.run(RatingApplication.class, args); + } + +} diff --git a/src/main/java/com/example/rating/RatingController.java b/src/main/java/com/example/rating/RatingController.java new file mode 100644 index 00000000..2845283a --- /dev/null +++ b/src/main/java/com/example/rating/RatingController.java @@ -0,0 +1,23 @@ +package com.example.rating; + +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.concurrent.ExecutionException; + +@RestController +@RequiredArgsConstructor +public class RatingController { + + private final RatingService ratingService; + + + @PostMapping("/fetch") + public ResponseEntity fetchAndSaveRatings() throws ExecutionException, InterruptedException, JsonProcessingException { + ratingService.fetchAndSaveRatings(); + return ResponseEntity.ok("Ratings fetched and saved successfully!"); + } +} diff --git a/src/main/java/com/example/rating/RatingRepository.java b/src/main/java/com/example/rating/RatingRepository.java new file mode 100644 index 00000000..66e97e99 --- /dev/null +++ b/src/main/java/com/example/rating/RatingRepository.java @@ -0,0 +1,8 @@ +package com.example.rating; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface RatingRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/rating/RatingService.java b/src/main/java/com/example/rating/RatingService.java new file mode 100644 index 00000000..47cd93bc --- /dev/null +++ b/src/main/java/com/example/rating/RatingService.java @@ -0,0 +1,108 @@ +package com.example.rating; + +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.http.HttpStatus; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static java.lang.System.exit; + +@Service +@RequiredArgsConstructor +public class RatingService { + private static final String FEED_URL = "https://realfood.tesco.com/api/feed/dataextract?authkey=fd2693ee-494e-40bd-8600-576d59293d46"; + + private final RatingRepository ratingRepository; + + public void fetchAndSaveRatings() throws InterruptedException, ExecutionException, JsonProcessingException { + RestTemplate restTemplate = new RestTemplate(); + CompletableFuture> responseFuture = CompletableFuture.supplyAsync(() -> { + try { + return restTemplate.getForEntity(FEED_URL, String.class); + } catch (Exception e) { + throw new RuntimeException("Failed to fetch data", e); + } + }); + + // Wait for the response asynchronously + ResponseEntity response = responseFuture.get(); + + if (response.getStatusCode() == HttpStatus.OK) { + final String responseBody = response.getBody(); + + // Parse JSON response using Jackson's streaming API for efficiency + final ObjectMapper objectMapper = new ObjectMapper(); + final List ratings = new ArrayList<>(); + JsonNode rootNode = objectMapper.readTree(responseBody); + // Iterate over each field in the root node + rootNode.fields().forEachRemaining(entry -> { + String key = entry.getKey(); // Key of the current field (if needed) + JsonNode recipeNode = entry.getValue(); // Value (the actual object) + + + // If the entry itself is an array, iterate through its elements + if (recipeNode.isArray()) { + for (JsonNode recipe : recipeNode) { + + // Check for the outputRecipe and its contents + if (recipe.has("outputRecipe")) { + JsonNode outputRecipe = recipe.get("outputRecipe"); + + // If outputRecipe is an array, process each element + + // Extract urlFriendlyTitle and rating + String urlFriendlyTitle = outputRecipe.has("urlFriendlyTitle") ? outputRecipe.get("urlFriendlyTitle").asText() : null; + JsonNode ratingNode = outputRecipe.get("rating"); + + if (ratingNode != null && ratingNode.has("average")) { + Double averageRating = ratingNode.get("average").asDouble(); + + // Validate urlFriendlyTitle and averageRating before processing + if (urlFriendlyTitle != null && !urlFriendlyTitle.trim().isEmpty() && averageRating != null) { + Rating rating = new Rating().builder() + .urlFriendlyTitle(urlFriendlyTitle) + .currentDay(LocalDate.now()) + .averageRating(String.valueOf(averageRating)) + .build(); + + // Save the Rating entity to the repository + ratings.add(rating); + } else { + System.out.println("Skipping outputRecipe with invalid data: " + outputRecipe); + } + } + } + + } + } else { + System.out.println("Entry is not an array: " + recipeNode); + } + }); + + + // Save ratings in batches to avoid individual DB calls for each entry + batchSaveRatings(ratings); + } else { + throw new RuntimeException("Failed to fetch data: " + response.getStatusCode()); + } + } + + // Optimized batch save method + public void batchSaveRatings(List ratings) { + if (!ratings.isEmpty()) { + // Assuming RatingRepository is set up for batch inserts + ratingRepository.saveAll(ratings); // Save all ratings at once (assuming it supports batch save) + } + } +} diff --git a/src/main/java/com/example/rating/Recipe.java b/src/main/java/com/example/rating/Recipe.java new file mode 100644 index 00000000..4bc2f838 --- /dev/null +++ b/src/main/java/com/example/rating/Recipe.java @@ -0,0 +1,23 @@ +package com.example.rating; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.Builder; +import lombok.Data; +import lombok.Setter; + +import java.util.UUID; + +@Data +@JsonIgnoreProperties +@Setter +@Builder +public class Recipe { + + private String urlFriendlyTitle; + + private Rating rating; +} diff --git a/src/main/java/com/example/rating/Test.java b/src/main/java/com/example/rating/Test.java new file mode 100644 index 00000000..0d565d1e --- /dev/null +++ b/src/main/java/com/example/rating/Test.java @@ -0,0 +1,17 @@ +package com.example.rating; + +public class Test { + + public static void main(String[] args) { + pending(9); + } + + + + private static int pending(int n) + { + return n <= 1 ? n : 2 * ( 1+n/2-pending(n/2)); + + + } +} diff --git a/src/main/java/com/example/rating/da-sent.bin b/src/main/java/com/example/rating/da-sent.bin new file mode 100644 index 00000000..9913d530 Binary files /dev/null and b/src/main/java/com/example/rating/da-sent.bin differ diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 00000000..c714d50b --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.datasource.url=jdbc:postgresql://localhost:5432/postgres +spring.datasource.username=postgres +spring.datasource.password=postgres +spring.datasource.driver-class-name=org.postgresql.Driver + +# Hibernate properties for JPA +spring.jpa.hibernate.ddl-auto=validate +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file diff --git a/src/test/java/com/example/rating/RatingApplicationTests.java b/src/test/java/com/example/rating/RatingApplicationTests.java new file mode 100644 index 00000000..d0268b82 --- /dev/null +++ b/src/test/java/com/example/rating/RatingApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.rating; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class RatingApplicationTests { + + @Test + void contextLoads() { + } + +}