V2 FOR MAH BABY
This commit is contained in:
50
.github/workflow/new-image.yml
vendored
Normal file
50
.github/workflow/new-image.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
name: Build and Push Quarkus Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ] # Pas aan naar jouw hoofdbranch als dit 'master' is
|
||||||
|
|
||||||
|
env:
|
||||||
|
# VERVANG DIT door de domeinnaam van jouw Gitea server (zonder https://)
|
||||||
|
REGISTRY: gitea.melvanveen.nl
|
||||||
|
# Dit pakt automatisch de eigenaar en naam van de repo (bijv. username/quarkus-app)
|
||||||
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
runs-on: ubuntu-latest # Zorg dat je Gitea runner Docker ondersteunt
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Code ophalen
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Java 21 instellen
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: '21' # Pas aan naar 17 als je Java 17 gebruikt
|
||||||
|
distribution: 'temurin'
|
||||||
|
cache: 'maven' # Dit versnelt volgende builds aanzienlijk
|
||||||
|
|
||||||
|
- name: Quarkus applicatie bouwen (.jar genereren)
|
||||||
|
run: ./mvnw clean package -DskipTests
|
||||||
|
|
||||||
|
- name: Inloggen op Gitea Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
# Gitea genereert automatisch dit token, net als GitHub.
|
||||||
|
# Deze heeft de rechten om packages te pushen.
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Docker image bouwen en pushen
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
# We gebruiken de standaard JVM Dockerfile van Quarkus
|
||||||
|
file: src/main/docker/Dockerfile.jvm
|
||||||
|
push: true
|
||||||
|
# We taggen de image met 'latest' én met de specifieke commit-hash
|
||||||
|
tags: |
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
|
||||||
2
pom.xml
2
pom.xml
@@ -12,7 +12,7 @@
|
|||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
||||||
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
||||||
<quarkus.platform.version>3.17.6</quarkus.platform.version>
|
<quarkus.platform.version>3.32.1</quarkus.platform.version>
|
||||||
<skipITs>true</skipITs>
|
<skipITs>true</skipITs>
|
||||||
<surefire-plugin.version>3.5.0</surefire-plugin.version>
|
<surefire-plugin.version>3.5.0</surefire-plugin.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|||||||
97
src/main/docker/Dockerfile.jvm
Normal file
97
src/main/docker/Dockerfile.jvm
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
####
|
||||||
|
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
|
||||||
|
#
|
||||||
|
# Before building the container image run:
|
||||||
|
#
|
||||||
|
# ./mvnw package
|
||||||
|
#
|
||||||
|
# Then, build the image with:
|
||||||
|
#
|
||||||
|
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/jobfindr-jvm .
|
||||||
|
#
|
||||||
|
# Then run the container using:
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/jobfindr-jvm
|
||||||
|
#
|
||||||
|
# If you want to include the debug port into your docker image
|
||||||
|
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
|
||||||
|
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
|
||||||
|
# when running the container
|
||||||
|
#
|
||||||
|
# Then run the container using :
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/jobfindr-jvm
|
||||||
|
#
|
||||||
|
# This image uses the `run-java.sh` script to run the application.
|
||||||
|
# This scripts computes the command line to execute your Java application, and
|
||||||
|
# includes memory/GC tuning.
|
||||||
|
# You can configure the behavior using the following environment properties:
|
||||||
|
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
|
||||||
|
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
|
||||||
|
# in JAVA_OPTS (example: "-Dsome.property=foo")
|
||||||
|
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
|
||||||
|
# used to calculate a default maximal heap memory based on a containers restriction.
|
||||||
|
# If used in a container without any memory constraints for the container then this
|
||||||
|
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
|
||||||
|
# of the container available memory as set here. The default is `50` which means 50%
|
||||||
|
# of the available memory is used as an upper boundary. You can skip this mechanism by
|
||||||
|
# setting this value to `0` in which case no `-Xmx` option is added.
|
||||||
|
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
|
||||||
|
# is used to calculate a default initial heap memory based on the maximum heap memory.
|
||||||
|
# If used in a container without any memory constraints for the container then this
|
||||||
|
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
|
||||||
|
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
|
||||||
|
# is used as the initial heap size. You can skip this mechanism by setting this value
|
||||||
|
# to `0` in which case no `-Xms` option is added (example: "25")
|
||||||
|
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
|
||||||
|
# This is used to calculate the maximum value of the initial heap memory. If used in
|
||||||
|
# a container without any memory constraints for the container then this option has
|
||||||
|
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
|
||||||
|
# here. The default is 4096MB which means the calculated value of `-Xms` never will
|
||||||
|
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
|
||||||
|
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
|
||||||
|
# when things are happening. This option, if set to true, will set
|
||||||
|
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
|
||||||
|
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
|
||||||
|
# true").
|
||||||
|
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
|
||||||
|
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
|
||||||
|
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
|
||||||
|
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
|
||||||
|
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
|
||||||
|
# (example: "20")
|
||||||
|
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
|
||||||
|
# (example: "40")
|
||||||
|
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
|
||||||
|
# (example: "4")
|
||||||
|
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
|
||||||
|
# previous GC times. (example: "90")
|
||||||
|
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
|
||||||
|
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
|
||||||
|
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
|
||||||
|
# contain the necessary JRE command-line options to specify the required GC, which
|
||||||
|
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
|
||||||
|
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
|
||||||
|
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
|
||||||
|
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
|
||||||
|
# accessed directly. (example: "foo.example.com,bar.example.com")
|
||||||
|
#
|
||||||
|
###
|
||||||
|
FROM registry.access.redhat.com/ubi8/openjdk-21:1.20
|
||||||
|
|
||||||
|
ENV LANGUAGE='en_US:en'
|
||||||
|
|
||||||
|
|
||||||
|
# We make four distinct layers so if there are application changes the library layers can be re-used
|
||||||
|
COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
|
||||||
|
COPY --chown=185 target/quarkus-app/*.jar /deployments/
|
||||||
|
COPY --chown=185 target/quarkus-app/app/ /deployments/app/
|
||||||
|
COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 185
|
||||||
|
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||||
|
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
|
||||||
|
|
||||||
@@ -11,7 +11,6 @@ public class Vacature {
|
|||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
private String titel;
|
private String titel;
|
||||||
private String locatie;
|
private String locatie;
|
||||||
private String url;
|
private String url;
|
||||||
@@ -19,6 +18,8 @@ public class Vacature {
|
|||||||
@OneToOne
|
@OneToOne
|
||||||
@JoinColumn(name = "detail_id")
|
@JoinColumn(name = "detail_id")
|
||||||
private VacatureDetail detail;
|
private VacatureDetail detail;
|
||||||
|
private String description;
|
||||||
|
private LocalDate closingDate;
|
||||||
|
|
||||||
public VacatureDetail getDetail() {
|
public VacatureDetail getDetail() {
|
||||||
return detail;
|
return detail;
|
||||||
@@ -53,5 +54,45 @@ public class Vacature {
|
|||||||
public void setDatum(LocalDate datum) {
|
public void setDatum(LocalDate datum) {
|
||||||
this.datum = datum;
|
this.datum = datum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitel(String titel) {
|
||||||
|
this.titel = titel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocatie(String locatie) {
|
||||||
|
this.locatie = locatie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getDatum() {
|
||||||
|
return datum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getClosingDate() {
|
||||||
|
return closingDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClosingDate(LocalDate closingDate) {
|
||||||
|
this.closingDate = closingDate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import jakarta.ws.rs.core.MediaType;
|
|||||||
import nl.veenm.jobfindr.domain.Vacature;
|
import nl.veenm.jobfindr.domain.Vacature;
|
||||||
import nl.veenm.jobfindr.services.VacatureService;
|
import nl.veenm.jobfindr.services.VacatureService;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Path("/vacatures")
|
@Path("/vacatures")
|
||||||
@@ -18,25 +17,25 @@ public class VacatureResource {
|
|||||||
VacatureService vacatureService;
|
VacatureService vacatureService;
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
public List<Vacature> getVacatures() throws IOException {
|
public List<Vacature> getVacatures() {
|
||||||
return vacatureService.getServices();
|
return vacatureService.getServices();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/test")
|
||||||
|
public void testEmails(){
|
||||||
|
vacatureService.checkAndSendNewVacatures();
|
||||||
|
}
|
||||||
|
|
||||||
@GET()
|
@GET()
|
||||||
@Path("/all")
|
@Path("/all")
|
||||||
public List<Vacature> getAllVacatures() throws IOException {
|
public List<Vacature> getAllVacatures() {
|
||||||
return vacatureService.getAllVacatures();
|
return vacatureService.getAllVacatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@PATCH
|
||||||
@Path("/detail")
|
|
||||||
public Vacature getVacature() throws IOException {
|
|
||||||
return vacatureService.getVacature();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/clean")
|
@Path("/clean")
|
||||||
public void cleanVacatures() throws IOException {
|
public void cleanVacatures() {
|
||||||
vacatureService.cleanVacatures();
|
vacatureService.cleanVacatures();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package nl.veenm.jobfindr.scrapers;
|
package nl.veenm.jobfindr.scrapers;
|
||||||
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import nl.veenm.jobfindr.domain.Vacature;
|
import nl.veenm.jobfindr.domain.Vacature;
|
||||||
import nl.veenm.jobfindr.domain.VacatureDetail;
|
|
||||||
import nl.veenm.jobfindr.repository.VacatureDetailRepository;
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
@@ -12,8 +9,9 @@ import org.jsoup.nodes.Element;
|
|||||||
import org.jsoup.select.Elements;
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -22,6 +20,8 @@ public class ScraperService {
|
|||||||
|
|
||||||
private static final Logger log = Logger.getLogger(ScraperService.class);
|
private static final Logger log = Logger.getLogger(ScraperService.class);
|
||||||
|
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||||
|
|
||||||
public List<Vacature> scrapeVacatures(String url) throws IOException {
|
public List<Vacature> scrapeVacatures(String url) throws IOException {
|
||||||
// Maak verbinding met de website
|
// Maak verbinding met de website
|
||||||
Document doc = Jsoup.connect(url).get();
|
Document doc = Jsoup.connect(url).get();
|
||||||
@@ -34,7 +34,7 @@ public class ScraperService {
|
|||||||
List<Vacature> vacatures = new ArrayList<>();
|
List<Vacature> vacatures = new ArrayList<>();
|
||||||
for (Element vacatureElement : vacatureElements) {
|
for (Element vacatureElement : vacatureElements) {
|
||||||
// Haal de titel en de link op
|
// Haal de titel en de link op
|
||||||
String title = vacatureElement.select("h4.small-header").text();
|
String title = vacatureElement.select("div.heading-6").text();
|
||||||
String link = vacatureElement.select("a.vacature-target").attr("data-target").replace("/split", "");
|
String link = vacatureElement.select("a.vacature-target").attr("data-target").replace("/split", "");
|
||||||
|
|
||||||
// Zorg ervoor dat de link volledig is
|
// Zorg ervoor dat de link volledig is
|
||||||
@@ -42,7 +42,7 @@ public class ScraperService {
|
|||||||
link = "https://www.meesterbaan.nl" + link;
|
link = "https://www.meesterbaan.nl" + link;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Haal de locatie op (zoek naar het eerstvolgende div met class 'location mt-1')
|
assert vacatureElement.parent() != null;
|
||||||
Element locationElement = vacatureElement.parent().selectFirst("div.location.mt-1");
|
Element locationElement = vacatureElement.parent().selectFirst("div.location.mt-1");
|
||||||
String location = locationElement != null ? locationElement.text() : "Onbekende locatie";
|
String location = locationElement != null ? locationElement.text() : "Onbekende locatie";
|
||||||
|
|
||||||
@@ -61,34 +61,19 @@ public class ScraperService {
|
|||||||
return vacatures.stream().filter(a -> !a.getLocatie().equals("Onbekende locatie")).collect(Collectors.toList());
|
return vacatures.stream().filter(a -> !a.getLocatie().equals("Onbekende locatie")).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vacature getVacatureDetails(Vacature vacature) throws IOException {
|
public void getVacatureDetails(Vacature vacature) throws IOException {
|
||||||
Document doc = Jsoup.connect(vacature.getUrl()).get();
|
Document doc = Jsoup.connect(vacature.getUrl()).get();
|
||||||
|
|
||||||
Elements vacatureElements = doc.select("div.list-property");
|
Elements vacatureElements = doc.select("div.rounded.gray-tag-md.px-2.py-2");
|
||||||
|
|
||||||
HashMap<String, String> details = new HashMap<>();
|
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
vacatureElements.forEach(element -> {
|
vacatureElements.forEach(element -> {
|
||||||
String detail = element.selectFirst("label").text();
|
sb.append(element.text()).append("<br>");
|
||||||
String value = element.selectXpath("div").text();
|
if (element.text().startsWith("Sluitingsdatum:")) {
|
||||||
|
vacature.setClosingDate(LocalDate.parse(element.text().replace("Sluitingsdatum: ", ""), formatter));
|
||||||
details.put(detail, value);
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
VacatureDetail detail = new VacatureDetail();
|
vacature.setDescription(sb.toString());
|
||||||
|
|
||||||
detail.setGeplaatst(details.get("Geplaatst") != null? details.get("Geplaatst") : "Onbekend");
|
|
||||||
detail.setSluitingsdatum(details.get("Sluitingsdatum")!= null? details.get("Sluitingsdatum") : "Onbekend");
|
|
||||||
detail.setSalaris(details.get("Salaris")!= null? details.get("Salaris") : "Onbekend");
|
|
||||||
detail.setAantalUren(details.get("Aantal uren")!= null? details.get("Aantal uren") : "Onbekend");
|
|
||||||
detail.setFte(details.get("FTE")!= null? details.get("FTE") : "Onbekend");
|
|
||||||
detail.setStartdatum(details.get("Startdatum")!= null? details.get("Startdatum") : "Onbekend");
|
|
||||||
detail.setSoortVacature(details.get("Soort vacature")!= null? details.get("Soort vacature") : "Onbekend");
|
|
||||||
detail.setDienstverband(details.get("Dienstverband")!= null? details.get("Dienstverband") : "Onbekend");
|
|
||||||
detail.setZijInstromers(details.get("Zij-instromers")!= null? details.get("Zij-instromers") : "Onbekend");
|
|
||||||
|
|
||||||
vacature.setDetail(detail);
|
|
||||||
|
|
||||||
return vacature;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,46 +33,39 @@ public class EmailService {
|
|||||||
emailBody.append("<html>")
|
emailBody.append("<html>")
|
||||||
.append("<head>")
|
.append("<head>")
|
||||||
.append("<style>")
|
.append("<style>")
|
||||||
.append("body { font-family: Arial, sans-serif; line-height: 1.6; }")
|
.append("body { font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background-color: #f4f7f6; margin: 0; padding: 20px; -webkit-font-smoothing: antialiased; }")
|
||||||
.append("h1 { color: #333; }")
|
.append(".email-container { max-width: 600px; margin: 0 auto; background-color: #ffffff; padding: 40px; border-radius: 16px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); }")
|
||||||
.append("h2 { color: #555; }")
|
.append("h1 { color: #2d3748; font-size: 26px; margin-top: 0; }")
|
||||||
.append("p { color: #666; }")
|
.append(".intro { color: #718096; font-size: 16px; margin-bottom: 30px; }")
|
||||||
.append("ul { padding-left: 20px; }")
|
.append(".vacature { margin-bottom: 25px; padding: 25px; border: 1px solid #edf2f7; border-radius: 12px; background-color: #ffffff; box-shadow: 0 2px 5px rgba(0,0,0,0.02); }")
|
||||||
.append("li { margin-bottom: 10px; }")
|
.append(".vacature h2 { margin: 0 0 10px 0; font-size: 20px; color: #2b6cb0; }")
|
||||||
.append(".vacature { margin-bottom: 20px; padding: 10px; border: 1px solid #ddd; border-radius: 5px; background-color: #f9f9f9; }")
|
.append(".vacature p { color: #4a5568; line-height: 1.6; margin-bottom: 20px; font-size: 15px; }")
|
||||||
.append(".vacature h2 { margin: 0; font-size: 18px; color: #333; }")
|
.append(".label { font-weight: 600; color: #a0aec0; text-transform: uppercase; letter-spacing: 1px; font-size: 12px; margin-bottom: 5px; display: block; }")
|
||||||
.append(".vacature p { margin: 5px 0; }")
|
.append(".footer { margin-top: 40px; padding-top: 20px; border-top: 2px dashed #edf2f7; font-size: 14px; color: #a0aec0; text-align: center; line-height: 1.5; }")
|
||||||
.append(".footer { margin-top: 30px; font-size: 14px; color: #999; }")
|
|
||||||
.append("</style>")
|
.append("</style>")
|
||||||
.append("</head>")
|
.append("</head>")
|
||||||
.append("<body>")
|
.append("<body>")
|
||||||
.append("<h1>Lieve Danthe,</h1>")
|
.append("<div class=\"email-container\">")
|
||||||
.append("<p>Hier zijn de nieuwste vacatures die ik heb gevonden:</p>")
|
.append("<h1>Lieve Danthe, ✨</h1>")
|
||||||
.append("<div>");
|
.append("<p class=\"intro\">Ik heb weer even voor je gezocht. Hier zijn de nieuwste vacatures die perfect bij jou passen:</p>");
|
||||||
|
|
||||||
for (Vacature vacature : vacatures) {
|
for (Vacature vacature : vacatures) {
|
||||||
emailBody.append("<div class=\"vacature\">")
|
emailBody.append("<div class=\"vacature\">")
|
||||||
.append("<h2>").append(vacature.getTitel()).append("</h2>")
|
.append("<h2>").append(vacature.getTitel()).append("</h2>")
|
||||||
.append("<p><strong>Locatie:</strong> ").append(vacature.getLocatie()).append("</p>")
|
.append("<span class=\"label\">Korte beschrijving</span>")
|
||||||
.append("<p><strong>Geplaatst:</strong> ").append(vacature.getDetail().getGeplaatst()).append("</p>")
|
.append("<p>").append(vacature.getDescription()).append("</p>")
|
||||||
.append("<p><strong>Sluitingsdatum:</strong> ").append(vacature.getDetail().getSluitingsdatum()).append("</p>")
|
.append("<div><a href=\"").append(vacature.getUrl())
|
||||||
.append("<p><strong>Salaris:</strong> ").append(vacature.getDetail().getSalaris()).append("</p>")
|
.append("\" style=\"display: inline-block; padding: 12px 24px; background-color: #ed8936; color: #ffffff; text-decoration: none; border-radius: 8px; font-weight: bold; font-size: 14px;\">")
|
||||||
.append("<p><strong>Aantal uren:</strong> ").append(vacature.getDetail().getAantalUren()).append("</p>")
|
.append("Bekijk vacature →</a></div>")
|
||||||
.append("<p><strong>FTE:</strong> ").append(vacature.getDetail().getFte()).append("</p>")
|
|
||||||
.append("<p><strong>Startdatum:</strong> ").append(vacature.getDetail().getStartdatum()).append("</p>")
|
|
||||||
.append("<p><strong>Soort vacature:</strong> ").append(vacature.getDetail().getSoortVacature()).append("</p>")
|
|
||||||
.append("<p><strong>Dienstverband:</strong> ").append(vacature.getDetail().getDienstverband()).append("</p>")
|
|
||||||
.append("<p><strong>Zij-instromers:</strong> ").append(vacature.getDetail().getZijInstromers()).append("</p>")
|
|
||||||
.append("<p><a href=\"").append(vacature.getUrl()).append("\" style=\"color: #007bff; text-decoration: none;\">Bekijk vacature</a></p>")
|
|
||||||
.append("</div>");
|
.append("</div>");
|
||||||
}
|
}
|
||||||
|
|
||||||
emailBody.append("</div>")
|
emailBody.append("<div class=\"footer\">")
|
||||||
.append("<div class=\"footer\">")
|
.append("Met heel veel liefde gebouwd,<br>")
|
||||||
.append("Met vriendelijke groet,<br>")
|
.append("<strong>Het vacatureteam</strong><br>")
|
||||||
.append("Het vacatureteam<br>")
|
.append("(a.k.a. je vriendje ❤️)")
|
||||||
.append("(a.k.a je vriendje)")
|
|
||||||
.append("</div>")
|
.append("</div>")
|
||||||
|
.append("</div>") // Einde email-container
|
||||||
.append("</body>")
|
.append("</body>")
|
||||||
.append("</html>");
|
.append("</html>");
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import org.jboss.logging.Logger;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.time.format.DateTimeParseException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -39,26 +37,19 @@ public class VacatureService {
|
|||||||
|
|
||||||
private final String className = this.getClass().getSimpleName();
|
private final String className = this.getClass().getSimpleName();
|
||||||
|
|
||||||
public List<Vacature> getServices() throws IOException {
|
public List<Vacature> getServices() {
|
||||||
eventService.logInfo(VacatureService.class.getName(), "getServices", "getServices aangeroepen");
|
eventService.logInfo(VacatureService.class.getName(), "getServices", "getServices aangeroepen");
|
||||||
List<Vacature> vacatures = new ArrayList<>();
|
List<Vacature> vacatures = getVacatures();
|
||||||
vacatures.addAll(scraperService.scrapeVacatures("https://www.meesterbaan.nl/vacatures/50-km?trefwoord=duits&locatie=Apeldoorn"));
|
|
||||||
vacatures.addAll(scraperService.scrapeVacatures("https://www.meesterbaan.nl/vacatures/50-km?trefwoord=frans&locatie=Apeldoorn"));
|
|
||||||
emailService.stuurVacatureEmail("vanveenmel11@gmail.com", vacatures);
|
emailService.stuurVacatureEmail("vanveenmel11@gmail.com", vacatures);
|
||||||
return vacatures;
|
return vacatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkAndSendNewVacatures() throws IOException {
|
@Transactional
|
||||||
|
public void checkAndSendNewVacatures() {
|
||||||
logger.info("Checking for new vacatures");
|
logger.info("Checking for new vacatures");
|
||||||
eventService.logInfo(className, "checkAndSendNewVacatures", "Checking for new vacatures");
|
eventService.logInfo(className, "checkAndSendNewVacatures", "Checking for new vacatures");
|
||||||
LocalDate today = LocalDate.now();
|
LocalDate today = LocalDate.now();
|
||||||
|
List<Vacature> todayVacatures = getVacatures();
|
||||||
// Scrape de nieuwste vacatures
|
|
||||||
String url = "https://www.meesterbaan.nl/vacatures/50-km?trefwoord=duits&locatie=Apeldoorn";
|
|
||||||
List<Vacature> todayVacatures = scraperService.scrapeVacatures(url);
|
|
||||||
|
|
||||||
url = "https://www.meesterbaan.nl/vacatures/50-km?trefwoord=frans&locatie=Apeldoorn";
|
|
||||||
todayVacatures.addAll(scraperService.scrapeVacatures(url));
|
|
||||||
|
|
||||||
todayVacatures.forEach(todayVacature -> {
|
todayVacatures.forEach(todayVacature -> {
|
||||||
try {
|
try {
|
||||||
@@ -71,25 +62,18 @@ public class VacatureService {
|
|||||||
// Haal de vacatures van gisteren op
|
// Haal de vacatures van gisteren op
|
||||||
List<Vacature> yesterdayVacatures = vacatureRepository.findAll().stream().toList();
|
List<Vacature> yesterdayVacatures = vacatureRepository.findAll().stream().toList();
|
||||||
|
|
||||||
System.out.println(todayVacatures.size());
|
|
||||||
System.out.println(yesterdayVacatures.size());
|
|
||||||
|
|
||||||
|
|
||||||
todayVacatures.removeIf(a -> yesterdayVacatures.stream().anyMatch(b -> b.getUrl().equals(a.getUrl())));
|
todayVacatures.removeIf(a -> yesterdayVacatures.stream().anyMatch(b -> b.getUrl().equals(a.getUrl())));
|
||||||
|
|
||||||
System.out.println(todayVacatures.size());
|
|
||||||
|
|
||||||
// Alleen nieuwe vacatures opslaan en versturen
|
// Alleen nieuwe vacatures opslaan en versturen
|
||||||
if (!todayVacatures.isEmpty()) {
|
if (!todayVacatures.isEmpty()) {
|
||||||
// Sla de nieuwe vacatures op
|
// Sla de nieuwe vacatures op
|
||||||
todayVacatures.forEach(vacature -> {
|
todayVacatures.forEach(vacature -> {
|
||||||
vacature.setDatum(today);
|
vacature.setDatum(today);
|
||||||
vacatureDetailRepository.persist(vacature.getDetail());
|
|
||||||
vacatureRepository.persist(vacature);
|
vacatureRepository.persist(vacature);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Verstuur een e-mail
|
// Verstuur een e-mail
|
||||||
emailService.stuurVacatureEmail("danthefranken@gmail.com", todayVacatures);
|
// emailService.stuurVacatureEmail("danthefranken@gmail.com", todayVacatures);
|
||||||
emailService.stuurVacatureEmail("vanveenmel11@gmail.com", todayVacatures);
|
emailService.stuurVacatureEmail("vanveenmel11@gmail.com", todayVacatures);
|
||||||
logger.info("Nieuwe vacatures verstuurd en opgeslagen.");
|
logger.info("Nieuwe vacatures verstuurd en opgeslagen.");
|
||||||
eventService.logSucces(className, "checkAndSendNewVacatures", "Nieuwe vacatures verstuurd en opgeslagen.");
|
eventService.logSucces(className, "checkAndSendNewVacatures", "Nieuwe vacatures verstuurd en opgeslagen.");
|
||||||
@@ -99,10 +83,28 @@ public class VacatureService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Vacature> getVacatures() {
|
||||||
|
List<String> urls = List.of(
|
||||||
|
"https://www.meesterbaan.nl/vacatures/plaats/apeldoorn/35-km?trefwoord=geschiedenis",
|
||||||
|
"https://www.meesterbaan.nl/vacatures/plaats/apeldoorn/35-km?trefwoord=Docent%20Geschiedenis"
|
||||||
|
);
|
||||||
|
List<Vacature> todayVacatures = new ArrayList<>();
|
||||||
|
|
||||||
|
// Scrape de nieuwste vacatures
|
||||||
|
urls.forEach(url -> {
|
||||||
|
try {
|
||||||
|
todayVacatures.addAll(scraperService.scrapeVacatures(url));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return todayVacatures;
|
||||||
|
}
|
||||||
|
|
||||||
@Scheduled(cron = "0 0 9 * * ?")
|
@Scheduled(cron = "0 0 9 * * ?")
|
||||||
@Scheduled(cron = "0 0 17 * * ?")
|
@Scheduled(cron = "0 0 17 * * ?")
|
||||||
@Transactional
|
@Transactional
|
||||||
void dagelijksControleerEnVerstuur() throws IOException {
|
void dagelijksControleerEnVerstuur() {
|
||||||
checkAndSendNewVacatures();
|
checkAndSendNewVacatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,41 +113,24 @@ public class VacatureService {
|
|||||||
return vacatureRepository.listAll();
|
return vacatureRepository.listAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vacature getVacature() throws IOException {
|
|
||||||
return scraperService.getVacatureDetails(new Vacature());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Scheduled(cron = "0 0 0 * * ?")
|
@Scheduled(cron = "0 0 0 * * ?")
|
||||||
@Transactional
|
@Transactional
|
||||||
public void cleanVacatures() {
|
public void cleanVacatures() {
|
||||||
logger.info("Cleaning vacatures");
|
logger.info("Cleaning vacatures");
|
||||||
eventService.logInfo(className, "cleanVacatures", "Cleaning vacatures");
|
eventService.logInfo(className, "cleanVacatures", "Cleaning vacatures");
|
||||||
getAllVacatures().forEach(vacature -> {
|
getAllVacatures().forEach(vacature -> {
|
||||||
var date = convertStringToLocalDate(vacature.getDetail().getSluitingsdatum());
|
var date = vacature.getClosingDate();
|
||||||
var today = LocalDate.now();
|
var today = LocalDate.now();
|
||||||
|
|
||||||
if (date != null) {
|
if (date.isBefore(today)) {
|
||||||
if (date.isBefore(today)) {
|
logger.info("Deleting vacature " + vacature.getTitel());
|
||||||
logger.info("Deleting vacature " + vacature.getTitel());
|
eventService.logInfo(className, "cleanVacatures", "Deleting vacature " + vacature.getTitel());
|
||||||
eventService.logInfo(className, "cleanVacatures", "Deleting vacature " + vacature.getTitel());
|
vacatureDetailRepository.delete(vacature.getDetail());
|
||||||
vacatureDetailRepository.delete(vacature.getDetail());
|
vacatureRepository.delete(vacature);
|
||||||
vacatureRepository.delete(vacature);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
eventService.logSucces(className, "cleanVacatures", "Cleaning vacatures done");
|
|
||||||
});
|
});
|
||||||
|
logger.info("Cleaning vacatures done");
|
||||||
|
eventService.logSucces(className, "cleanVacatures", "Cleaning vacatures done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static LocalDate convertStringToLocalDate(String dateString) {
|
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
|
||||||
try {
|
|
||||||
return LocalDate.parse(dateString, formatter);
|
|
||||||
} catch (DateTimeParseException e) {
|
|
||||||
logger.error("Ongeldige datumstring: " + dateString);
|
|
||||||
return null; // Of gooi een aangepaste exception als dat gewenst is
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user