update prompt and website style

This commit is contained in:
2025-05-10 17:35:51 +01:00
parent 3b00a71391
commit 1b3b01e90e
6 changed files with 129 additions and 13 deletions

13
.idea/runConfigurations/Dockerfile.xml generated Normal file
View File

@@ -0,0 +1,13 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Dockerfile" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="containerName" value="" />
<option name="commandLineOptions" value="--env-file .env -v $(pwd)/data.sqlite:/app/data.sqlite" />
<option name="showCommandPreview" value="true" />
<option name="sourceFilePath" value="Dockerfile" />
</settings>
</deployment>
<method v="2" />
</configuration>
</component>

23
Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
FROM golang:1.24 as builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY *.go ./
COPY web ./web
RUN CGO_ENABLED=0 GOOS=linux go build -o ./server
FROM gcr.io/distroless/base-debian11 AS build-release-stage
COPY --from=builder /app/server /app/server
USER nonroot:nonroot
WORKDIR /app
EXPOSE 8080
ENTRYPOINT ["./server"]

10
docker-compose.yml Normal file
View File

@@ -0,0 +1,10 @@
services:
server:
build:
dockerfile: ./Dockerfile
env_file:
- .env
ports:
- "8080:8080"
volumes:
- ./data.sqlite:/app/data.sqlite

75
main.go
View File

@@ -89,7 +89,57 @@ type state struct {
//go:embed web
var webDir embed.FS
var prompt = "The current time is 7am. Provide a summary of today's weather in %v below, as well as how to deal with the weather, such as how to dress for the weather, and whether they need an umbrella Use celsius and fahrenheit for temperature. Mention %v in the summary, but don't add anything else, as the summary will be displayed on a website."
var envKeys = []string{"GEMINI_API_KEY", "OPEN_WEATHER_MAP_API_KEY", "VAPID_PRIVATE_KEY_BASE64", "VAPID_PUBLIC_KEY_BASE64"}
var prompt = `The current time is 5pm. Provide a short summary of the weather forecast for the next 48 hours in JSON in %v below.
This is the JSON schema of the JSON:
current Current weather data API response
current.dt Current time, Unix, UTC
current.sunrise Sunrise time, Unix, UTC. For polar areas in midnight sun and polar night periods this parameter is not returned in the response
current.sunset Sunset time, Unix, UTC. For polar areas in midnight sun and polar night periods this parameter is not returned in the response
current.temp Temperature. Units - default: kelvin, metric: Celsius, imperial: Fahrenheit. How to change units used
current.feels_like Temperature. This temperature parameter accounts for the human perception of weather. Units default: kelvin, metric: Celsius, imperial: Fahrenheit.
current.pressure Atmospheric pressure on the sea level, hPa
current.humidity Humidity, percentage
current.dew_point Atmospheric temperature (varying according to pressure and humidity) below which water droplets begin to condense and dew can form. Units default: kelvin, metric: Celsius, imperial: Fahrenheit
current.clouds Cloudiness, percentage
current.uvi Current UV index.
current.visibility Average visibility, metres. The maximum value of the visibility is 10 km
current.wind_speed Wind speed. Wind speed. Units default: metre/sec, metric: metre/sec, imperial: miles/hour. How to change units used
current.wind_gust (where available) Wind gust. Units default: metre/sec, metric: metre/sec, imperial: miles/hour. How to change units used
current.wind_deg Wind direction, degrees (meteorological)
current.rain.1h (where available) Precipitation, mm/h. Please note that only mm/h as units of measurement are available for this parameter
current.snow.1h (where available) Precipitation, mm/h. Please note that only mm/h as units of measurement are available for this parameter
current.weather
current.weather.id Weather condition id
current.weather.main Group of weather parameters (Rain, Snow etc.)
current.weather.description Weather condition within the group (full list of weather conditions). Get the output in your language
current.weather.icon
hourly Hourly forecast weather data API response
hourly.dt Time of the forecasted data, Unix, UTC
hourly.temp Temperature. Units default: kelvin, metric: Celsius, imperial: Fahrenheit. How to change units used
hourly.feels_like Temperature. This accounts for the human perception of weather. Units default: kelvin, metric: Celsius, imperial: Fahrenheit.
hourly.pressure Atmospheric pressure on the sea level, hPa
hourly.humidity Humidity, percentage
hourly.dew_point Atmospheric temperature (varying according to pressure and humidity) below which water droplets begin to condense and dew can form. Units default: kelvin, metric: Celsius, imperial: Fahrenheit.
hourly.uvi UV index
hourly.clouds Cloudiness, percentage
hourly.visibility Average visibility, metres. The maximum value of the visibility is 10 km
hourly.wind_speed Wind speed. Units default: metre/sec, metric: metre/sec, imperial: miles/hour.How to change units used
hourly.wind_gust (where available) Wind gust. Units default: metre/sec, metric: metre/sec, imperial: miles/hour. How to change units used
hourly.wind_deg Wind direction, degrees (meteorological)
hourly.pop Probability of precipitation. The values of the parameter vary between 0 and 1, where 0 is equal to 0 percent, 1 is equal to 100 percent
hourly.rain.1h (where available) Precipitation, mm/h. Please note that only mm/h as units of measurement are available for this parameter
hourly.snow.1h (where available) Precipitation, mm/h. Please note that only mm/h as units of measurement are available for this parameter
hourly.weather.id Weather condition id
hourly.weather.main Group of weather parameters (Rain, Snow etc.)
hourly.weather.description Weather condition within the group (full list of weather conditions). Get the output in your language
Summarize hourly weather until midnight today. Keep it concise. Suggest how to deal with the weather, such as how to dress for the weather, and whether they need an umbrella.
Use celsius and fahrenheit but not Kelvin for temperature. Mention %v in the summary, but don't add anything else, as the summary will be displayed on a website.
The summary should be in plaintext for humans. Do not output in JSON.
`
var supportedLocations = map[string]location{
"london": {51.507351, -0.127758, "Europe/London"},
@@ -120,9 +170,10 @@ func main() {
return
}
err := godotenv.Load()
_ = godotenv.Load()
err := checkEnv()
if err != nil {
log.Fatalln("please create a .env file using the provided template!")
log.Fatal(err)
}
db, err := initDB()
@@ -229,6 +280,20 @@ func generateKeys() {
fmt.Printf("private key: %v\n", priv)
}
func checkEnv() error {
var missing []string
for _, k := range envKeys {
v := os.Getenv(k)
if v == "" {
missing = append(missing, k)
}
}
if len(missing) > 0 {
return fmt.Errorf("missing env: %v", strings.Join(missing, ", "))
}
return nil
}
func handleHTTPRequest(state *state) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
path := strings.TrimPrefix(request.URL.Path, "/")
@@ -463,7 +528,7 @@ func deleteSubscription(state *state, regID uuid.UUID) error {
func updateSummaries(state *state, locKey string, loc *location) {
log.Printf("updating summary for %v...\n", locKey)
resp, err := http.Get(fmt.Sprintf("https://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&appid=%v", loc.lat, loc.lon, state.apiKey.openWeatherMap))
resp, err := http.Get(fmt.Sprintf("https://api.openweathermap.org/data/3.0/onecall?lat=%v&lon=%v&exclude=minutely,daily&appid=%v", loc.lat, loc.lon, state.apiKey.openWeatherMap))
if err != nil {
log.Printf("error updating summaries for %s: %e\n", locKey, err)
return
@@ -478,7 +543,7 @@ func updateSummaries(state *state, locKey string, loc *location) {
result, err := state.genai.Models.GenerateContent(state.ctx, "gemini-2.0-flash", []*genai.Content{{
Parts: []*genai.Part{
{Text: fmt.Sprintf(prompt, locationNames[locKey])},
{Text: fmt.Sprintf(prompt, locationNames[locKey], locationNames[locKey])},
{Text: string(b)},
},
}}, nil)

View File

@@ -1,8 +1,8 @@
:root {
--background-color: #f3f4f6;
--text-color: #1f2937;
--button-color: #030712;
--button-text-color: #f3f4f6;
--button-color: #d1d5db;
--button-text-color: #1f2937;
}
@media (prefers-color-scheme: dark) {
@@ -81,10 +81,12 @@ main {
button {
background-color: var(--button-color);
color: var(--button-text-color);
border: 0;
padding: 1rem 3rem;
border: 1px #9ca3af solid;
padding: 0.5rem 2rem;
border-radius: 4px;
font-weight: bold;
width: 100%;
font-weight: 500;
box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px;
}
footer {
@@ -102,8 +104,11 @@ footer > p {
}
.summary {
max-width: 30ch;
font-size: 2em;
max-width: 40ch;
font-size: 1em;
line-height: 1.5em;
padding-top: 1rem;
padding-bottom: 1rem;
}
.back-link {

View File

@@ -15,7 +15,7 @@
<body>
<div class="container">
<main>
<a href="/">&lt;- All locations</a>
<a class="back-link" href="/">&lt;- All locations</a>
<p class="summary">{{.Summary}}</p>
<button type="button" id="get-summary-btn" data-loc="{{.Location}}">Get daily updates at 7am</button>
</main>