initial commit with first prototype
This commit is contained in:
commit
871c276342
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@ -0,0 +1,3 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.env
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.env
|
||||
19
Dockerfile
Normal file
19
Dockerfile
Normal 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"]
|
||||
1
already_suggested_companies.txt
Normal file
1
already_suggested_companies.txt
Normal file
@ -0,0 +1 @@
|
||||
Bereits vorgeschlagene Unternehmen
|
||||
127
cv_sebastian_egli.txt
Normal file
127
cv_sebastian_egli.txt
Normal 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
164
job_suggestor.ipynb
Normal 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
100
job_suggestor.py
Normal 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
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
google-genai
|
||||
pydantic
|
||||
Loading…
Reference in New Issue
Block a user