initial commit with first prototype

This commit is contained in:
Sebastian Egli 2025-08-28 17:19:54 +02:00
commit 871c276342
8 changed files with 417 additions and 0 deletions

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
__pycache__/
*.pyc
.env

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.env

19
Dockerfile Normal file
View File

@ -0,0 +1,19 @@
# 1. Use a lightweight Python image
FROM python:3.12-slim
# 2. Environment settings
ENV PYTHONUNBUFFERED=1
ENV PIP_NO_CACHE_DIR=1
# 3. Set working directory inside the container
WORKDIR /app
# 4. Copy and install dependencies first (better caching)
COPY requirements.txt .
RUN pip install -r requirements.txt
# 5. Copy all your app files into the container
COPY . .
# 6. Set the default command to run your script
ENTRYPOINT ["python", "job_suggestor.py"]

View File

@ -0,0 +1 @@
Bereits vorgeschlagene Unternehmen

127
cv_sebastian_egli.txt Normal file
View File

@ -0,0 +1,127 @@
Dr. SebastianEgli
Geoinformatiker
Person
Geb: 18.01.1986
Nat: Deutsch, Schweiz
Adresse
A. d. Schülerhecke 13
35037 Marburg
Deutschland
Tel & E-Mail
+49 174 2465052
seb.egli@gmail.com
Schwerpunkte
Business Intelligence
Datenanalyse
Erdbeobachtung (EO)
Maschinelles Lernen
Agrarmodellierung
Klimatologie
Werkzeuge
QGIS, GDAL, OGR
Pandas, Numpy
Xarray, Satpy
Scikit-learn
Tensorflow, Mxnet
Open Data Cube
STAC, ERPNext
airtable, metabase
n8n, Kobo, Python
Java, R, Kotlin
Sprachen
Deutsch
Englisch
Französisch
Berufserfahrung
01/14 - heute Mitgründer & Entwickler
Bridgesoft GbR, Marburg, Deutschland
Entwicklung mobiler Anwendungen für mittelständische Unternehmen. Part-
ner: PC Gärtner GmbH, Ökobox-Online, Bosshammersch Hof, Infralytic
GmbH
09/22 - 07/25 Chief Data Scientist
agriBORA GmbH, Deutschland/Kenia
Teamleitung, Projekt- und Produktkonzeption, Landwirtschaftliches Moni-
toring und Modellierung, satellitengestützte Feldflächenerkennung,
Ertragsprognose, Kreditbewertung mittels maschinellem Lernen
11/19 - 08/22 Wissenschaftlicher Mitarbeiter LCRS, Philipps-Universität Marburg, Deutschland
Atmosphärische Fernerkundung, Softwareentwicklung für Lehrzwecke in
der Atmosphärenwissenschaft, Entwicklung von Anwendungen für das
Waldmanagement mittels UAV-Einsatz, Maschinelles Lernen
06/15 - 05/19 Wissenschaftlicher Mitarbeiter LCRS, Philipps-Universität Marburg, Deutschland
Erkennung von Wolken und Nebel in geostationären Satellitendaten, Ent-
wicklung von Fernerkundungsalgorithmen, Integration von Anwendungen
des maschinellen Lernens
03/13 - 01/14 Wissenschaftlicher Mitarbeiter LCRS, Philipps-Universität Marburg, Deutschland
Untersuchung der vertikalen Verteilung mikrophysikalischer Eigenschaften
in Strahlungsnebeln mittels Radarprofilierung und ballongetragenen Profil-
messungen.
05/11 - 01/13 Studentische Hilfskraft
LCRS, Philipps-Universität Marburg, Deutschland
Satellitendatenakquise, Verwaltung operationeller Stationsdaten der Mar-
burger Ground Truth- und Profiling-Station, Entwicklung von Werkzeugen
zur automatischen Datenkonvertierung verschiedener atmosphärischer
Messinstrumente.
02/11 - 04/11 Praktikant
Deutscher Wetterdienst (DWD), Offenbach, Deutschland
Erfassung verfügbarer In-situ-Hageldaten beim DWD, Durchführbarkeits-
analyse einer Hagelklimatologie, Analyse räumlich-zeitlicher Variationen
der Hagelhäufigkeit und Erstellung von Hagelrisikokarten.
02/09 - 04/09 Praktikant
Bundesamt für Kartographie und Geodäsie (BKG), Frankfurt, Deutschland
Entwicklung eines digitalen Landschaftsmodells (Maßstab: 1:1.000.000)
auf Grundlage der beim BKG verwendeten digitalen topografischen Kar-
tenbasis.
Preise
10/20 Wissenschaftspreis 2019
Industrie- und Handelskammer Kassel-Marburg
Der Preis wird zur Würdigung herausragender Dissertationen verliehen, die
eine hohe Relevanz für wirtschaftliche Fragestellungen aufweisen.
Verliehen für die Dissertation: “Satellite-Based Fog Detection: A Dynamic
Retrieval Method for Europe Based on Machine Learning“
Ausbildung
06/15 - 05/19 Promotion in Geographie
LCRS, Philipps-Universität Marburg, Deutschland
Hauptthemen: Nebelerkennung, satellitengestützte Fernerkundung,
maschinelles Lernen
Titel der Dissertation: “Satellite-Based Fog Detection: A Dynamic Retrieval
Method for Europe Based on Machine Learning“ (Note: 1,0)
10/10 - 01/13 M.Sc. Umweltgeographie
Philipps-Universität Marburg, Deutschland
Hauptfach: Physische Geographie
Nebenfach: Informatik
Titel der Masterarbeit: “The influence of drop size distributions on the rela-
tionship between liquid water content and radar reflectivity“ (Note: 15,0)
10/08 - 09/10 B.Sc. Geographie
Philipps-Universität Marburg, Deutschland
Hauptfach: Physische Geographie
Nebenfächer: Geologie & Informatik
Zusätzliches Modul: Friedens- und Konfliktforschung
Titel der Bachelorarbeit: “Validation of the simulation results of the winter
storm Kyrill“ (Note: 13,0)
08/06 - 07/08 Bachelorstudium in Geographie Universität Zürich, Schweiz
Hauptfächer: Kartographie, Klimatologie & GIS
09/96 - 07/05 Weiterführende Schule Klettgau-Gymnasium Tiengen, Deutschland
Abschluss: Abitur (Note: 11,0)
09/92 - 07/96 Grundschule Grundschule Kadelburg, Deutschland

164
job_suggestor.ipynb Normal file
View File

@ -0,0 +1,164 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "cc3d4ef7",
"metadata": {},
"outputs": [],
"source": [
"from google import genai\n",
"from google.genai import types\n",
"from datetime import datetime\n",
"from pydantic import BaseModel\n",
"import os\n",
"\n",
"class JobSuggestion(BaseModel):\n",
" company_name: str\n",
" company_description: str\n",
" job_title: str\n",
" application_text: str\n",
"\n",
"# Load CV from file\n",
"with open(\"cv_sebastian_egli.txt\", \"r\", encoding=\"utf-8\") as f:\n",
" cv = f.read()\n",
"\n",
"# Load already suggested companies from file (to avoid duplicates)\n",
"with open(\"already_suggested_companies.txt\", \"r\", encoding=\"utf-8\") as f:\n",
" already_suggested_companies = f.read()\n",
"\n",
"# Define boolean for even-numbered days (I want to switch un/solicited every day)\n",
"today = datetime.today()\n",
"unsolicited_application = today.day % 2 == 0\n",
"\n",
"if unsolicited_application:\n",
" query = [\"\"\"Analysiere meinen Lebenslauf (cv) und die Liste bereits vorgeschlagener Unternehmen (already_suggested_companies). \n",
"Schlage mir genau ein Unternehmen in der Nähe von Marburg vor, das noch nicht in already_suggested_companies enthalten ist \n",
"und für eine Initiativbewerbung besonders gut zu meinem Profil passt. \n",
"\n",
"Erkläre in maximal 150 Wörtern prägnant, warum dieses Unternehmen eine besonders gute Wahl für mich wäre. \n",
"Formuliere die Antwort als kurze, professionelle Email mit dem Titel: 'Neuer Jobvorschlag'. \n",
"Begründe die Eignung anhand meiner Qualifikationen und Interessen. \n",
"Füge außerdem einen einzelnen Satz hinzu, der direkt in einem Bewerbungsschreiben genutzt werden könnte, \n",
"um die Übereinstimmung zwischen mir und dem Unternehmen hervorzuheben. \n",
"\n",
"Liefere nur die Email, keine zusätzliche Erklärung.\"\"\",already_suggested_companies,cv]\n",
"else:\n",
" query = [\"\"\"Analysiere meinen Lebenslauf (cv) und die Liste bereits vorgeschlagener Unternehmen (already_suggested_companies). \n",
"Schlage mir genau eine aktuell offene Stellenausschreibung in der Nähe von Marburg vor für ein Unternehmen, das noch nicht in already_suggested_companies enthalten ist \n",
"und die besonders gut zu meinem Profil passt. \n",
"\n",
"Erkläre in maximal 150 Wörtern prägnant, warum diese Stelle eine besonders gute Wahl für mich wäre. \n",
"Formuliere die Antwort als kurze, professionelle Email mit dem Titel: 'Neuer Jobvorschlag'. \n",
"Begründe die Eignung anhand meiner Qualifikationen und Interessen. \n",
"Füge außerdem einen einzelnen Satz hinzu, der direkt in einem Bewerbungsschreiben genutzt werden könnte, \n",
"um die Übereinstimmung zwischen mir und der ausgeschriebenen Stelle hervorzuheben. \n",
"\n",
"Liefere nur die Email, keine zusätzliche Erklärung.\"\"\",already_suggested_companies,cv]"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "7c68f66d",
"metadata": {},
"outputs": [],
"source": [
"api_key = os.environ.get(\"GEMINI_API_KEY\")\n",
"\n",
"client = genai.Client(api_key=api_key)\n",
"\n",
"# Step 1: grounded search (necessary for live web search)\n",
"resp_grounded = client.models.generate_content(\n",
" model=\"gemini-2.5-flash\",\n",
" contents=query,\n",
" config=types.GenerateContentConfig(\n",
" tools=[types.Tool(google_search=types.GoogleSearch())]\n",
" ),\n",
")\n",
"\n",
"grounded_answer = resp_grounded.text # freeform answer"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "fe54c9a2",
"metadata": {},
"outputs": [],
"source": [
"# Step 2: convert to JSON\n",
"resp_json = client.models.generate_content(\n",
" model=\"gemini-2.5-flash\",\n",
" contents=f\"Turn this answer into JSON: {grounded_answer}\",\n",
" config=types.GenerateContentConfig(\n",
" response_mime_type=\"application/json\",\n",
" response_schema=JobSuggestion,\n",
" ),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "476ae4e4",
"metadata": {},
"outputs": [],
"source": [
"# Append the response to already_suggested_companies.txt\n",
"with open(\"already_suggested_companies.txt\", \"a\", encoding=\"utf-8\") as f:\n",
" f.write(\"\\n\" + resp_json.parsed.company_name)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "c3d72c4f",
"metadata": {},
"outputs": [],
"source": [
"import smtplib\n",
"from email.mime.text import MIMEText\n",
"\n",
"gmail_key = os.environ.get(\"GMAIL_PW\")\n",
"\n",
"# Account details\n",
"sender = \"seb.egli@gmail.com\"\n",
"recipient = \"seb.egli@gmail.com\"\n",
"password = gmail_key\n",
"\n",
"# Create email\n",
"msg = MIMEText(grounded_answer)\n",
"msg[\"From\"] = sender\n",
"msg[\"To\"] = recipient\n",
"msg[\"Subject\"] = \"Neuer Jobvorschlag\"\n",
"\n",
"# Send email using Gmail's SMTP\n",
"with smtplib.SMTP_SSL(\"smtp.gmail.com\", 465) as server:\n",
" server.login(sender, password)\n",
" server.sendmail(sender, recipient, msg.as_string())"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "odc-landsat",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

100
job_suggestor.py Normal file
View File

@ -0,0 +1,100 @@
from google import genai
from google.genai import types
from datetime import datetime
from pydantic import BaseModel
import smtplib
from email.mime.text import MIMEText
import os
# Load keys from environment variables
api_key = os.environ.get("GEMINI_API_KEY")
gmail_key = os.environ.get("GMAIL_PW")
class JobSuggestion(BaseModel):
company_name: str
company_description: str
job_title: str
application_text: str
# Load CV from file
with open("cv_sebastian_egli.txt", "r", encoding="utf-8") as f:
cv = f.read()
# Load already suggested companies from file (to avoid duplicates)
with open("already_suggested_companies.txt", "r", encoding="utf-8") as f:
already_suggested_companies = f.read()
# Define boolean for even-numbered days (I want to switch un/solicited every day)
today = datetime.today()
unsolicited_application = today.day % 2 == 0
if unsolicited_application:
query = ["""Analysiere meinen Lebenslauf (cv) und die Liste bereits vorgeschlagener Unternehmen (already_suggested_companies).
Schlage mir genau ein Unternehmen in der Nähe von Marburg vor, das noch nicht in already_suggested_companies enthalten ist
und für eine Initiativbewerbung besonders gut zu meinem Profil passt.
Erkläre in maximal 150 Wörtern prägnant, warum dieses Unternehmen eine besonders gute Wahl für mich wäre.
Formuliere die Antwort als kurze, professionelle Email mit dem Titel: 'Neuer Jobvorschlag'.
Begründe die Eignung anhand meiner Qualifikationen und Interessen.
Füge außerdem einen einzelnen Satz hinzu, der direkt in einem Bewerbungsschreiben genutzt werden könnte,
um die Übereinstimmung zwischen mir und dem Unternehmen hervorzuheben.
Liefere nur die Email, keine zusätzliche Erklärung.""",already_suggested_companies,cv]
else:
query = ["""Analysiere meinen Lebenslauf (cv) und die Liste bereits vorgeschlagener Unternehmen (already_suggested_companies).
Schlage mir genau eine aktuell offene Stellenausschreibung in der Nähe von Marburg vor für ein Unternehmen, das noch nicht in already_suggested_companies enthalten ist
und die besonders gut zu meinem Profil passt.
Erkläre in maximal 150 Wörtern prägnant, warum diese Stelle eine besonders gute Wahl für mich wäre.
Formuliere die Antwort als kurze, professionelle Email mit dem Titel: 'Neuer Jobvorschlag'.
Begründe die Eignung anhand meiner Qualifikationen und Interessen.
Füge außerdem einen einzelnen Satz hinzu, der direkt in einem Bewerbungsschreiben genutzt werden könnte,
um die Übereinstimmung zwischen mir und der ausgeschriebenen Stelle hervorzuheben.
Liefere nur die Email, keine zusätzliche Erklärung.""",already_suggested_companies,cv]
# Get response from Gemini
# Initialize Gemini client
client = genai.Client(api_key=api_key)
# Step 1: grounded search (necessary for live web search)
resp_grounded = client.models.generate_content(
model="gemini-2.5-flash",
contents=query,
config=types.GenerateContentConfig(
tools=[types.Tool(google_search=types.GoogleSearch())]
),
)
grounded_answer = resp_grounded.text # freeform answer
# Step 2: convert to JSON
resp_json = client.models.generate_content(
model="gemini-2.5-flash",
contents=f"Turn this answer into JSON: {grounded_answer}",
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=JobSuggestion,
),
)
# Append the response to already_suggested_companies.txt
with open("already_suggested_companies.txt", "a", encoding="utf-8") as f:
f.write("\n" + resp_json.parsed.company_name)
# Send email with the suggestion
# Account details
sender = "seb.egli@gmail.com"
recipient = "seb.egli@gmail.com"
password = gmail_key
# Create email
msg = MIMEText(grounded_answer)
msg["From"] = sender
msg["To"] = recipient
msg["Subject"] = "Neuer Jobvorschlag"
# Send email using Gmail's SMTP
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(sender, password)
server.sendmail(sender, recipient, msg.as_string())

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
google-genai
pydantic