init
This commit is contained in:
57
src/main/java/nl/veenm/jobfindr/domain/Vacature.java
Normal file
57
src/main/java/nl/veenm/jobfindr/domain/Vacature.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package nl.veenm.jobfindr.domain;
|
||||
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
public class Vacature {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String titel;
|
||||
private String locatie;
|
||||
private String url;
|
||||
private LocalDate datum;
|
||||
@OneToOne
|
||||
@JoinColumn(name = "detail_id")
|
||||
private VacatureDetail detail;
|
||||
|
||||
public VacatureDetail getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
public void setDetail(VacatureDetail detail) {
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
public Vacature(String title, String link, String location) {
|
||||
this.titel = title;
|
||||
this.url = link;
|
||||
this.locatie = location;
|
||||
}
|
||||
|
||||
public Vacature() {
|
||||
|
||||
}
|
||||
|
||||
public String getTitel() {
|
||||
return titel;
|
||||
}
|
||||
|
||||
public String getLocatie() {
|
||||
return locatie;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setDatum(LocalDate datum) {
|
||||
this.datum = datum;
|
||||
}
|
||||
}
|
||||
|
||||
113
src/main/java/nl/veenm/jobfindr/domain/VacatureDetail.java
Normal file
113
src/main/java/nl/veenm/jobfindr/domain/VacatureDetail.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package nl.veenm.jobfindr.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
|
||||
@Entity
|
||||
public class VacatureDetail {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String geplaatst;
|
||||
private String sluitingsdatum;
|
||||
private String salaris;
|
||||
private String aantalUren;
|
||||
private String fte;
|
||||
private String startdatum;
|
||||
private String soortVacature;
|
||||
private String dienstverband;
|
||||
private String zijInstromers;
|
||||
|
||||
public VacatureDetail() {
|
||||
}
|
||||
|
||||
public String getGeplaatst() {
|
||||
return geplaatst;
|
||||
}
|
||||
|
||||
public void setGeplaatst(String geplaatst) {
|
||||
this.geplaatst = geplaatst;
|
||||
}
|
||||
|
||||
public String getSluitingsdatum() {
|
||||
return sluitingsdatum;
|
||||
}
|
||||
|
||||
public void setSluitingsdatum(String sluitingsdatum) {
|
||||
this.sluitingsdatum = sluitingsdatum;
|
||||
}
|
||||
|
||||
public String getSalaris() {
|
||||
return salaris;
|
||||
}
|
||||
|
||||
public void setSalaris(String salaris) {
|
||||
this.salaris = salaris;
|
||||
}
|
||||
|
||||
public String getAantalUren() {
|
||||
return aantalUren;
|
||||
}
|
||||
|
||||
public void setAantalUren(String aantalUren) {
|
||||
this.aantalUren = aantalUren;
|
||||
}
|
||||
|
||||
public String getFte() {
|
||||
return fte;
|
||||
}
|
||||
|
||||
public void setFte(String fte) {
|
||||
this.fte = fte;
|
||||
}
|
||||
|
||||
public String getStartdatum() {
|
||||
return startdatum;
|
||||
}
|
||||
|
||||
public void setStartdatum(String startdatum) {
|
||||
this.startdatum = startdatum;
|
||||
}
|
||||
|
||||
public String getSoortVacature() {
|
||||
return soortVacature;
|
||||
}
|
||||
|
||||
public void setSoortVacature(String soortVacature) {
|
||||
this.soortVacature = soortVacature;
|
||||
}
|
||||
|
||||
public String getDienstverband() {
|
||||
return dienstverband;
|
||||
}
|
||||
|
||||
public void setDienstverband(String dienstverband) {
|
||||
this.dienstverband = dienstverband;
|
||||
}
|
||||
|
||||
public String getZijInstromers() {
|
||||
return zijInstromers;
|
||||
}
|
||||
|
||||
public void setZijInstromers(String zijInstromers) {
|
||||
this.zijInstromers = zijInstromers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "VacatureDetail{" +
|
||||
"Geplaatst='" + geplaatst + '\'' +
|
||||
", Sluitingsdatum='" + sluitingsdatum + '\'' +
|
||||
", Salaris='" + salaris + '\'' +
|
||||
", Aantal uren='" + aantalUren + '\'' +
|
||||
", FTE='" + fte + '\'' +
|
||||
", Startdatum='" + startdatum + '\'' +
|
||||
", Soort vacature='" + soortVacature + '\'' +
|
||||
", Dienstverband='" + dienstverband + '\'' +
|
||||
", Zij-instromers='" + zijInstromers + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package nl.veenm.jobfindr.repository;
|
||||
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import nl.veenm.jobfindr.domain.VacatureDetail;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@ApplicationScoped
|
||||
public class VacatureDetailRepository implements PanacheRepository<VacatureDetail> {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package nl.veenm.jobfindr.repository;
|
||||
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import nl.veenm.jobfindr.domain.Vacature;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@ApplicationScoped
|
||||
public class VacatureRepository implements PanacheRepository<Vacature> {
|
||||
|
||||
public List<Vacature> findByDate(LocalDate date) {
|
||||
return list("datum", date);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package nl.veenm.jobfindr.resources;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import nl.veenm.jobfindr.domain.Vacature;
|
||||
import nl.veenm.jobfindr.services.VacatureService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@Path("/vacatures")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public class VacatureResource {
|
||||
|
||||
@Inject
|
||||
VacatureService vacatureService;
|
||||
|
||||
@GET
|
||||
public List<Vacature> getVacatures() throws IOException {
|
||||
return vacatureService.getServices();
|
||||
}
|
||||
|
||||
@GET()
|
||||
@Path("/all")
|
||||
public List<Vacature> getAllVacatures() throws IOException {
|
||||
return vacatureService.getAllVacatures();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/detail")
|
||||
public Vacature getVacature() throws IOException {
|
||||
return vacatureService.getVacature();
|
||||
}
|
||||
}
|
||||
89
src/main/java/nl/veenm/jobfindr/scrapers/ScraperService.java
Normal file
89
src/main/java/nl/veenm/jobfindr/scrapers/ScraperService.java
Normal file
@@ -0,0 +1,89 @@
|
||||
package nl.veenm.jobfindr.scrapers;
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import nl.veenm.jobfindr.domain.Vacature;
|
||||
import nl.veenm.jobfindr.domain.VacatureDetail;
|
||||
import nl.veenm.jobfindr.repository.VacatureDetailRepository;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ApplicationScoped
|
||||
public class ScraperService {
|
||||
|
||||
public List<Vacature> scrapeVacatures(String url) throws IOException {
|
||||
// Maak verbinding met de website
|
||||
Document doc = Jsoup.connect(url).get();
|
||||
|
||||
// Selecteer alle vacature-elementen
|
||||
Elements vacatureElements = doc.select("div.mt-2.text-overflow-ellipsis");
|
||||
|
||||
List<Vacature> vacatures = new ArrayList<>();
|
||||
for (Element vacatureElement : vacatureElements) {
|
||||
// Haal de titel en de link op
|
||||
String title = vacatureElement.select("h4.small-header").text();
|
||||
String link = vacatureElement.select("a.vacature-target").attr("data-target").replace("/split", "");
|
||||
|
||||
// Zorg ervoor dat de link volledig is
|
||||
if (!link.startsWith("http")) {
|
||||
link = "https://www.meesterbaan.nl" + link;
|
||||
}
|
||||
|
||||
// Haal de locatie op (zoek naar het eerstvolgende div met class 'location mt-1')
|
||||
Element locationElement = vacatureElement.parent().selectFirst("div.location.mt-1");
|
||||
String location = locationElement != null ? locationElement.text() : "Onbekende locatie";
|
||||
|
||||
// Maak een Vacature-object en voeg het toe aan de lijst
|
||||
vacatures.add(new Vacature(title, link, location));
|
||||
}
|
||||
|
||||
vacatures.forEach(vacature -> {
|
||||
try {
|
||||
getVacatureDetails(vacature);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
return vacatures.stream().filter(a -> !a.getLocatie().equals("Onbekende locatie")).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Vacature getVacatureDetails(Vacature vacature) throws IOException {
|
||||
Document doc = Jsoup.connect(vacature.getUrl()).get();
|
||||
|
||||
Elements vacatureElements = doc.select("div.list-property");
|
||||
|
||||
HashMap<String, String> details = new HashMap<>();
|
||||
|
||||
vacatureElements.forEach(element -> {
|
||||
String detail = element.selectFirst("label").text();
|
||||
String value = element.selectXpath("div").text();
|
||||
|
||||
details.put(detail, value);
|
||||
});
|
||||
|
||||
VacatureDetail detail = new VacatureDetail();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
57
src/main/java/nl/veenm/jobfindr/services/EmailService.java
Normal file
57
src/main/java/nl/veenm/jobfindr/services/EmailService.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package nl.veenm.jobfindr.services;
|
||||
|
||||
import io.quarkus.mailer.Mail;
|
||||
import io.quarkus.mailer.Mailer;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import nl.veenm.jobfindr.domain.Vacature;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@ApplicationScoped
|
||||
public class EmailService {
|
||||
|
||||
private final Mailer mailer;
|
||||
|
||||
public EmailService(Mailer mailer) {
|
||||
this.mailer = mailer;
|
||||
}
|
||||
|
||||
public void stuurVacatureEmail(String recipient, List<Vacature> vacatures) {
|
||||
// Bouw de inhoud van de e-mail
|
||||
StringBuilder emailBody = new StringBuilder();
|
||||
emailBody.append("Lieve Danthe,\n\n");
|
||||
emailBody.append("Hier zijn de nieuwste vacatures die ik heb gevonden:\n\n");
|
||||
|
||||
for (Vacature vacature : vacatures) {
|
||||
emailBody.append("- ").append(vacature.getTitel()).append("\n");
|
||||
emailBody.append(" Locatie: ").append(vacature.getLocatie()).append("\n");
|
||||
emailBody.append(" Details: ").append("\n");
|
||||
emailBody.append(" -Geplaatst: ").append(vacature.getDetail().getGeplaatst()).append("\n");
|
||||
emailBody.append(" -Sluitingsdatum: ").append(vacature.getDetail().getSluitingsdatum()).append("\n");
|
||||
emailBody.append(" -Salaris: ").append(vacature.getDetail().getSalaris()).append("\n");
|
||||
emailBody.append(" -Aantal uren: ").append(vacature.getDetail().getAantalUren()).append("\n");
|
||||
emailBody.append(" -FTE: ").append(vacature.getDetail().getFte()).append("\n");
|
||||
emailBody.append(" -Startdatum: ").append(vacature.getDetail().getStartdatum()).append("\n");
|
||||
emailBody.append(" -Soort vacature: ").append(vacature.getDetail().getSoortVacature()).append("\n");
|
||||
emailBody.append(" -Dienstverband: ").append(vacature.getDetail().getDienstverband()).append("\n");
|
||||
emailBody.append(" -Zij-instromers: ").append(vacature.getDetail().getZijInstromers()).append("\n");
|
||||
emailBody.append(" Link: ").append(vacature.getUrl()).append("\n\n");
|
||||
}
|
||||
|
||||
emailBody.append("Met vriendelijke groet,\n");
|
||||
emailBody.append("Het vacatureteam\n");
|
||||
emailBody.append("(a.k.a je vriendje)");
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String date = now.getDayOfYear() + "-" + now.getMonth().getValue();
|
||||
|
||||
String subject = "[%s] Nieuwe vacatures gevonden";
|
||||
|
||||
// Verstuur de e-mail
|
||||
mailer.send(Mail.withText(recipient, String.format(subject, date), emailBody.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
104
src/main/java/nl/veenm/jobfindr/services/VacatureService.java
Normal file
104
src/main/java/nl/veenm/jobfindr/services/VacatureService.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package nl.veenm.jobfindr.services;
|
||||
|
||||
import io.quarkus.scheduler.Scheduled;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import nl.veenm.jobfindr.domain.Vacature;
|
||||
import nl.veenm.jobfindr.repository.VacatureDetailRepository;
|
||||
import nl.veenm.jobfindr.repository.VacatureRepository;
|
||||
import nl.veenm.jobfindr.scrapers.ScraperService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ApplicationScoped
|
||||
public class VacatureService {
|
||||
@Inject
|
||||
ScraperService scraperService;
|
||||
|
||||
@Inject
|
||||
EmailService emailService;
|
||||
|
||||
@Inject
|
||||
VacatureRepository vacatureRepository;
|
||||
|
||||
@Inject
|
||||
VacatureDetailRepository vacatureDetailRepository;
|
||||
|
||||
public List<Vacature> getServices() throws IOException {
|
||||
List<Vacature> vacatures = new ArrayList<>();
|
||||
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("danthefranken@gmail.com", vacatures);
|
||||
return vacatures;
|
||||
}
|
||||
|
||||
public void checkAndSendNewVacatures() throws IOException {
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDate yesterday = today.minusDays(1);
|
||||
|
||||
// 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 -> {
|
||||
try {
|
||||
scraperService.getVacatureDetails(todayVacature);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
// Haal de vacatures van gisteren op
|
||||
List<Vacature> yesterdayVacatures = vacatureRepository.findByDate(yesterday);
|
||||
|
||||
// Filter nieuwe vacatures
|
||||
List<Vacature> newVacatures = todayVacatures.stream()
|
||||
.filter(vacature -> yesterdayVacatures.stream()
|
||||
.noneMatch(yv -> yv.getTitel().equals(vacature.getTitel()) && yv.getUrl().equals(vacature.getUrl())))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Alleen nieuwe vacatures opslaan en versturen
|
||||
if (!newVacatures.isEmpty()) {
|
||||
// Sla de nieuwe vacatures op
|
||||
newVacatures.forEach(vacature -> {
|
||||
System.out.println(newVacatures.size());
|
||||
vacature.setDatum(today);
|
||||
vacatureDetailRepository.persist(vacature.getDetail());
|
||||
vacatureRepository.persist(vacature);
|
||||
});
|
||||
|
||||
// Verstuur een e-mail
|
||||
// emailService.stuurVacatureEmail("danthefranken@gmail.com", newVacatures);
|
||||
emailService.stuurVacatureEmail("vanveenmel11@gmail.com", newVacatures);
|
||||
System.out.println("Nieuwe vacatures verstuurd en opgeslagen.");
|
||||
} else {
|
||||
System.out.println("Geen nieuwe vacatures gevonden.");
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 9 * * ?")
|
||||
@Scheduled(cron = "0 53 13 * * ?")
|
||||
@Scheduled(cron = "0 0 17 * * ?")
|
||||
@Transactional
|
||||
void dagelijksControleerEnVerstuur() throws IOException {
|
||||
checkAndSendNewVacatures();
|
||||
}
|
||||
|
||||
public List<Vacature> getAllVacatures() {
|
||||
return vacatureRepository.listAll();
|
||||
}
|
||||
|
||||
public Vacature getVacature() throws IOException {
|
||||
return scraperService.getVacatureDetails(new Vacature());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user