Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Translations #152

Open
2 tasks done
cloneofghosts opened this issue Feb 9, 2024 · 21 comments · May be fixed by #383
Open
2 tasks done

Translations #152

cloneofghosts opened this issue Feb 9, 2024 · 21 comments · May be fixed by #383
Labels
enhancement New feature or request priority: high
Milestone

Comments

@cloneofghosts
Copy link
Collaborator

cloneofghosts commented Feb 9, 2024

Describe the feature

This was something I posted in #48 but maybe something for the 2.0 version you could get the translations working and then down the line you could add in the text summaries?

You could make have something like this for English (not including things needed for the summaries):

English Translations
  • Clear
  • Mostly Clear
  • Partly Cloudy
  • Mostly Cloudy
  • Cloudy
  • Drizzle
  • Light Rain
  • Rain
  • Heavy Rain
  • Flurries
  • Light Snow
  • Snow
  • Heavy Snow
  • Sleet
  • Freezing Drizzle
  • Freezing Rain
  • Mixed Precipitation
  • Rain and Snow
  • Unknown Precipitation
  • Thunderstorm
  • Heavy Thunderstorm
  • Hail
  • Possible Thunderstorms
  • Possible Rain
  • Possible Snow
  • Possible Sleet
  • Possible Freezing Rain
  • Breezy
  • Windy
  • Foggy
  • Mist
  • Hazy
  • Smoke

I'm aware that some of these strings aren't in the translation module but it might be good to define them in case they get used in the future? Even if you just have the summaries of the conditions for now it would be a good starting point.

Acknowledgements

  • I have searched this repository and Home Assistant Repository to see if the feature has already been requested.
  • I have written an informative title.
@alexander0042
Copy link
Collaborator

Good to open this issue to add it to the dashboard. Writing a note here to remind myself to check the old Dark Sky translations library to see if it'll work with my current basic text implementation, or if I need to have the full summary string working before I can leverage off that.

@cloneofghosts
Copy link
Collaborator Author

cloneofghosts commented Feb 22, 2024

While I don't know how you have the text implementation setup I think it should work with the translation library but if not you could always remove anything you're not using and add it back when you do the text summaries.

I also didn't notice this until it was mentioned in the BriefSky issue but DarkSky did give an approximation on how they had their weather conditions setup https://github.com/Pirate-Weather/translations?tab=readme-ov-file#weather-conditions

EDIT: No idea what the plans are for the translations repo but I created a PR to fix a bunch of issues related to dependencies and created a lint action as well.

@cloneofghosts
Copy link
Collaborator Author

@alexander0042 I was able to figure out something for this. I found a pip module which allows you to integrate node.js into python. I was able to figure out their translation module and came up with this test script:

from javascript import require, globalThis

Translations = require("path_to_translation_folder")

lang = "en"
translation = Translations[lang];

if translation is None:
	translation = Translations["en"]

cText = translation.translate(["sentence", ["during", "possible-light-rain", "later-today-night"]])
print(cText)

cText = translation.translate(["title", ["light-clouds"]])
print(cText)

Running it in the terminal I get:

Possible light rain later tonight.
Partly Cloudy

If you want to just translate the conditions you have now and wait to setup the other ones when you do the text summaries that's fine but its your call.

@alexander0042
Copy link
Collaborator

Yes! Yes! Yes! This is easily the best "awaiting developer response" tag I could imagine. I figured something like that pip module must exist, but I didn't find that one out there. The fact that you got it working is even cooler.

What I'm thinking is making a new Python script that takes a currently, hourly, daily, or multi day forecast and generates the cText to feed to the node module and back. So it'd go main response -> generate text -> node -> generate text -> main response. That way it should be easier to maintain and would be more organized, since the response script is already pretty long.

@cloneofghosts
Copy link
Collaborator Author

Maybe for 2.4 you could translate the existing conditions and then expand upon it for 2.5?

@cloneofghosts
Copy link
Collaborator Author

cloneofghosts commented Nov 19, 2024

I decided to re-create my comment from before as I was able to figure more things out. Was able to get it to work using the GitHub repository we have setup.

This is my example.py file:

example.py
def calculate_text(vis, visUnits, wind, windUnit, precipIntensity, prepIntensityUnit, precipType, cloudCover, isDayTime, mode="title"):
	if ((precipIntensity >= (0.02 * prepIntensityUnit)) & (precipType != None)):
		cIcon = precipType

		if precipType == 'rain':
			if precipIntensity < (0.4 * prepIntensityUnit):
				cText = [mode, "very-light-rain"]
				cCond = "very-light-rain"
			elif precipIntensity >= (0.4 * prepIntensityUnit) and precipIntensity < (2.5 * prepIntensityUnit):
				cText = [mode, "light-rain"]
				cCond = "light-rain"
			elif precipIntensity >= (2.5 * prepIntensityUnit) and precipIntensity < (10 * prepIntensityUnit):
				cText = [mode, "medium-rain"]
				cCond = "medium-rain"
			else:
				cText = [mode, "heavy-rain"]
				cCond = "heavy-rain"
		elif precipType == 'snow':
			if precipIntensity < (0.13 * prepIntensityUnit):
				cText = [mode, "very-light-snow"]
				cCond = "very-light-snow"
			elif precipIntensity >= (0.13 * prepIntensityUnit) and precipIntensity < (0.83 * prepIntensityUnit):
				cText = [mode, "light-snow"]
				cCond = "light-snow"
			elif precipIntensity >= (0.83 * prepIntensityUnit) and precipIntensity < (3.33 * prepIntensityUnit):
				cText = [mode, "medium-snow"]
				cCond = "medium-snow"
			else:
				cText = [mode, "heavy-snow"]
				cCond = "heavy-snow"
		elif precipType == 'sleet':
			if precipIntensity < (0.83 * prepIntensityUnit):
				cText = [mode, "very-light-sleet"]
				cCond = "very-light-sleet"
			elif precipIntensity >= (0.83 * prepIntensityUnit) and precipIntensity < (3.33 * prepIntensityUnit):
				cText = [mode, "medium-sleet"]
				cCond = "medium-sleet"
			else:
				cText = [mode, "heavy-sleet"]
				cCond = "heavy-sleet"
		else:
			if precipIntensity < (0.4 * prepIntensityUnit):
				cText = [mode, "very-light-precipitation"]
				cCond = "very-light-precipitation"
			elif precipIntensity >= (0.4 * prepIntensityUnit) and precipIntensity < (2.5 * prepIntensityUnit):
				cText = [mode, "light-precipitation"]
				cCond = "light-precipitation"
			elif precipIntensity >= (2.5 * prepIntensityUnit) and precipIntensity < (10 * prepIntensityUnit):
				cText = [mode, "medium-precipitation"]
				cCond = "medium-precipitation"
			else:
				cText = [mode, "heavy-precipitation"]
				cCond = "heavy-precipitation"

    # Because soemtimes there's precipitation not no type, don't use an icon in those cases

    # If visibility <1km and during the day
    # elif vis<1000 and (InterPcurrent[0]>InterPday[0,16] and InterPcurrent[0]<InterPday[0,17]):
	elif vis < (1000 * visUnits):
		return [mode, "fog"], "fog"
	elif cloudCover > 0.875:
		cText = [mode, "heavy-clouds"]
		cCond = "heavy-clouds"
		cIcon = "cloudy"
	elif cloudCover > 0.375:
		if isDayTime:
			cIcon = "partly-cloudy-day"
		else:
			cIcon = "partly-cloudy-night"
		if cloudCover > 0.625:
			cText = [mode, "medium-clouds"]
			cCond = "medium-clouds"
		else:
			cText = [mode, "light-clouds"]
			cCond = "light-clouds"
	else:
		if isDayTime:
			cIcon = "clear-day"
		else:
			cIcon = "clear-night"
		if cloudCover > 0.125:
			cText = [mode, "very-light-clouds"]
			cCond = "very-light-clouds"
		else:
			cText = [mode, "clear"]

	# Show the wind icon if there is no precipitation.
	if precipIntensity < 0.02:
	    # Show the wind text before the sky text
	    if wind >= (6.7056 * windUnit):
	    	cIcon = 'wind'
	    	cText = [mode, ["and", "light-wind", cCond]]
	    elif wind >= (10 * windUnit):
	    	cIcon = 'wind'
	    	cText = [mode, ["and", "medium-wind", cCond]]
	    elif wind >= (17.8816 * windUnit):
	    	cIcon = 'wind'
	    	cText = [mode, ["and", "heavy-wind", cCond]]
	else:
	    # Show the precipitation text before the wind text
	    if wind >= (17.8816 * windUnit):
	    	cText = [mode, ["and", cCond, "heavy-wind"]]
	    elif wind >= (10 * windUnit):
	    	cText = [mode, ["and", cCond, "medium-wind"]]
	    elif wind >= (6.7056 * windUnit):
	    	cText = [mode, ["and", cCond, "light-wind"]]
	return cText, cIcon


from javascript import require

Translations = require("./node_modules/translations/index.js")

lang = "en"
translation = Translations[lang];

if translation is None:
	translation = Translations["en"]

summary_text, cIcon = calculate_text(0.44, 0.001, 28.93, 3.6, 0.72, 1, "snow", 0.92, True)
cText = translation.translate(summary_text)
print(cText)
print(cIcon)

summary_text, cIcon = calculate_text(2.01, 0.001, 37.26, 3.6, 0.4339, 1, "rain", 0.92, True, "sentence")
cText = translation.translate(summary_text)
print(cText)
print(cIcon)

Then this is what I have in my package.json file

{
  "dependencies": {
    "translations": "github:Pirate-Weather/translations"
  }
}

In order for it to work you need to have this installed:

  • node.js
  • python
  • JSPyBridge

When you run the script in python it will install the node module and create the package.lock file for you and then run the script once everything is installed and good to go. Not sure how it would work on your AWS setup but it works locally for me and same goes for how it will grab the latest version from GitHub.

Tested with values from Regina, SK and it gave me Light Snow and Breezy with the icon being snow. Also tested with my location and get Mostly Clear with the icon being clear-day.

Also did a few more tests and the day/night check is working so it should be safe to use for both summary and icon.

@cloneofghosts
Copy link
Collaborator Author

cloneofghosts commented Nov 24, 2024

I may have went a bit crazy here but I came up with some code which generates the Possible Precipitation text under these conditions:

  • If probability of precipitation is between 10-30%
  • If probability of precipitation greater than 10% and precipIntensity is less than 0.02 mm/h

If either of those conditions are met then it adds the possible text in front of the very light and light precipitation text. For some reason Dark Sky didn't have possible precipitation texts for the medium and heavy precipitation which is why its not added there.

In this case the icon is still set to the default state of None and we have a summary text then it calculates the icon. If fog then show fog otherwise it will show the sky condition as the wind icon is still calculated above it.

Possible Precipitation Code
cloudy = 0.875
mostly_cloudy = 0.625
partly_cloudy = 0.375
mostly_clear = 0.125
visibility = 1000

def calculate_sky_icon(cloudCover, isDayTime):
	sky_icon = None

	if cloudCover > cloudy:
		sky_icon = "cloudy"
	elif cloudCover > partly_cloudy:
		if isDayTime:
			sky_icon = "partly-cloudy-day"
		else:
			sky_icon = "partly-cloudy-night"
	else:
		if isDayTime:
			sky_icon = "clear-day"
		else:
			sky_icon = "clear-night"

	return sky_icon

def calculate_text(vis, visUnits, wind, windUnit, precipIntensity, prepIntensityUnit, precipType, cloudCover, isDayTime, pop=1, mode="title"):
	possiblePrecip = ""
	cIcon = None
	cText = None

	# Add the possible precipitation text if pop is between 10-30% or if pop is greater than 10% but precipIntensity is between 0-0.02 mm/h
	if(pop >= 0.1 and pop < 0.3) or (pop >= 0.1 and precipIntensity > 0 and precipIntensity < (0.02 * prepIntensityUnit)):
		possiblePrecip = "possible-"
	else:
		cIcon = precipType

	# If precipIntensity is greater than 0.02 mm/h and no type fallback to rain icon
	if precipType == "none" and precipIntensity >= (0.02 * prepIntensityUnit):
		cIcon = "rain"

	if ((precipIntensity > 0) & (precipType != None)):
		if precipType == 'rain':
			if precipIntensity < (0.4 * prepIntensityUnit):
				cText = [mode, possiblePrecip + "very-light-rain"]
				cCond = possiblePrecip + "very-light-rain"
			elif precipIntensity >= (0.4 * prepIntensityUnit) and precipIntensity < (2.5 * prepIntensityUnit):
				cText = [mode, possiblePrecip + "light-rain"]
				cCond = possiblePrecip + "light-rain"
			elif precipIntensity >= (2.5 * prepIntensityUnit) and precipIntensity < (10 * prepIntensityUnit):
				cText = [mode, "medium-rain"]
				cCond = "medium-rain"
			else:
				cText = [mode, "heavy-rain"]
				cCond = "heavy-rain"
		elif precipType == 'snow':
			if precipIntensity < (0.13 * prepIntensityUnit):
				cText = [mode, possiblePrecip + "very-light-snow"]
				cCond = possiblePrecip + "very-light-snow"
			elif precipIntensity >= (0.13 * prepIntensityUnit) and precipIntensity < (0.83 * prepIntensityUnit):
				cText = [mode, possiblePrecip + "light-snow"]
				cCond = possiblePrecip + "light-snow"
			elif precipIntensity >= (0.83 * prepIntensityUnit) and precipIntensity < (3.33 * prepIntensityUnit):
				cText = [mode, "medium-snow"]
				cCond = "medium-snow"
			else:
				cText = [mode, "heavy-snow"]
				cCond = "heavy-snow"
		elif precipType == 'sleet':
			if precipIntensity < (0.83 * prepIntensityUnit):
				cText = [mode, possiblePrecip + "very-light-sleet"]
				cCond = possiblePrecip + "very-light-sleet"
			elif precipIntensity >= (0.83 * prepIntensityUnit) and precipIntensity < (3.33 * prepIntensityUnit):
				cText = [mode, "medium-sleet"]
				cCond = "medium-sleet"
			else:
				cText = [mode, "heavy-sleet"]
				cCond = "heavy-sleet"
		else:
			# Because soemtimes there's precipitation not no type use a generic precipitation summary
			if precipIntensity < (0.4 * prepIntensityUnit):
				cText = [mode, possiblePrecip + "very-light-precipitation"]
				cCond = possiblePrecip + "very-light-precipitation"
			elif precipIntensity >= (0.4 * prepIntensityUnit) and precipIntensity < (2.5 * prepIntensityUnit):
				cText = [mode, possiblePrecip + "light-precipitation"]
				cCond = possiblePrecip + "light-precipitation"
			elif precipIntensity >= (2.5 * prepIntensityUnit) and precipIntensity < (10 * prepIntensityUnit):
				cText = [mode, "medium-precipitation"]
				cCond = "medium-precipitation"
			else:
				cText = [mode, "heavy-precipitation"]
				cCond = "heavy-precipitation"

    # If visibility < 1000m
	elif vis < (visibility * visUnits):
		return [mode, "fog"], "fog"
	elif cloudCover > cloudy:
		cText = [mode, "heavy-clouds"]
		cCond = "heavy-clouds"
		cIcon = calculate_sky_icon(cloudCover, isDayTime)
	elif cloudCover > partly_cloudy:
		cIcon = calculate_sky_icon(cloudCover, isDayTime)
		if cloudCover > mostly_cloudy:
			cText = [mode, "medium-clouds"]
			cCond = "medium-clouds"
		else:
			cText = [mode, "light-clouds"]
			cCond = "light-clouds"
	else:
		cIcon = calculate_sky_icon(cloudCover, isDayTime)
		if cloudCover > mostly_clear:
			cText = [mode, "very-light-clouds"]
			cCond = "very-light-clouds"
		else:
			cText = [mode, "clear"]

	# Show the wind icon if there is no precipitation.
	if precipIntensity < 0.02:
	    # Show the wind text before the sky text
	    if wind >= (6.7056 * windUnit):
	    	cIcon = 'wind'
	    	cText = [mode, ["and", "light-wind", cCond]]
	    elif wind >= (10 * windUnit):
	    	cIcon = 'wind'
	    	cText = [mode, ["and", "medium-wind", cCond]]
	    elif wind >= (17.8816 * windUnit):
	    	cIcon = 'wind'
	    	cText = [mode, ["and", "heavy-wind", cCond]]
	else:
	    # Show the precipitation text before the wind text
	    if wind >= (17.8816 * windUnit):
	    	cText = [mode, ["and", cCond, "heavy-wind"]]
	    elif wind >= (10 * windUnit):
	    	cText = [mode, ["and", cCond, "medium-wind"]]
	    elif wind >= (6.7056 * windUnit):
	    	cText = [mode, ["and", cCond, "light-wind"]]

	# If we have a condition text but no icon then use the sky cover icon
	if cIcon is None and cText is not None:
		if vis < (visibility * visUnits):
			cIcon = "fog"
		elif cloudCover > cloudy:
			cIcon = calculate_sky_icon(cloudCover, isDayTime)
		elif cloudCover > partly_cloudy:
			cIcon = calculate_sky_icon(cloudCover, isDayTime)
		else:
			cIcon = calculate_sky_icon(cloudCover, isDayTime)
	return cText, cIcon


from javascript import require

Translations = require("./node_modules/translations/index.js")

lang = "en"
translation = Translations[lang];

if translation is None:
	translation = Translations["en"]

summary_text, cIcon = calculate_text(16.09, 0.001, 23.52, 3.6, 0, 1, "none", 0.31, True)
cText = translation.translate(summary_text)
print(cText)
print(cIcon)

summary_text, cIcon = calculate_text(11.82, 0.001, 11.25, 3.6, 0.0169, 1, "rain", 0.99, True)
cText = translation.translate(summary_text)
print(cText)
print(cIcon)

I've tested and its working as expected and feel free to make any changes you'd like or use the other function if you prefer that one.

Looking through the list of translations that are missing is Dry and Humid. Not sure how the translation was setup in Dark Sky and I wasn't sure how to set them up myself.

@alexander0042
Copy link
Collaborator

This is an amazing start, I love it! If you check out the dockerfiles, you'll see that I just pushed a new version that incorporates these new packages, and I've gotten it working on my test instance!

One thing I'm doing now is creating a public share that has a copy of all the production .zip files. The crazy resource requirements for the API are all on the data ingest, but there's no reason people should have to spin all that up to just improve the results, since 95% of the code is on the response side

@cloneofghosts
Copy link
Collaborator Author

This is an amazing start, I love it! If you check out the dockerfiles, you'll see that I just pushed a new version that incorporates these new packages, and I've gotten it working on my test instance!

Good to know you were able to get it working and it was helpful. The only translations I didn't setup was Dry and Humid. I would assume Dry is something to do with humidity and Humid something to do with humidity + apparent temperature? The translation module just says when its unusually dry and unusually humid for both of those texts which isn't really super useful.

I assume this isn't on the dev endpoint yet as I either see V2.3.3 or get a 502 error.

@alexander0042
Copy link
Collaborator

I should host it on AWS, but for now my own server should work well enough:
https://files.alexanderrey.ca/share/9jMgSLpi. I'm just working out the details now, but my plan is in the documentation for the code repository to have instruction to create a conda environment, then it's much easier to mess around with the response script!

@cloneofghosts
Copy link
Collaborator Author

I'm just working out the details now, but my plan is in the documentation for the code repository to have instruction to create a conda environment, then it's much easier to mess around with the response script!

Does running the script via conda reduce the system requirements or is it the same just a different environment?

As for the translations - is this something that will be included in V2.4 but it isn't working on the AWS server atm? As mentioned I get a 502 error or V2.3.3 when I try on the dev endpoint.

I was also thinking of adding these to the existing translations:

Hail
Possible Freezing Drizzle
Freezing Drizzle
Freezing Rain (do we want light/heavy freezing rain as well?)
Possible Freezing Rain
Missing Possible versions of other intensities (Possible Rain, Possible Snow, etc.)

I think that's good for now as we can always add in more translations afterwards if needed.

@alexander0042
Copy link
Collaborator

alexander0042 commented Nov 26, 2024

The api endpoint is just restarting now, so should be on 2.4.0 in the next hour or so!

For the translations, I have something working! Building extensively off your script, I end up with a calculate_text function like this:

Currently and Hourly Text
#%% Script to contain the functions that can be used to generate the text summary of the forecast data for Pirate Weather

from javascript import require

Translations = require("./node_modules/translations/index.js")

cloudThreshold = 0.875
mostlyCloudyThreshold = 0.625
partlyCloudyThreshold = 0.375
mostlyClearThreshold = 0.125


def calculate_sky_icon(cloudCover, isDayTime):
	sky_icon = None

	if cloudCover > cloudThreshold:
		sky_icon = "cloudy"
	elif cloudCover > mostlyCloudyThreshold:
		if isDayTime:
			sky_icon = "partly-cloudy-day"
		else:
			sky_icon = "partly-cloudy-night"
	else:
		if isDayTime:
			sky_icon = "clear-day"
		else:
			sky_icon = "clear-night"

	return sky_icon

def calculate_text(hourObject, prepIntensityUnit, visUnits, windUnit, isDayTime, mode="title"):

	visThresh  = 1000 * visUnits

	lightRainThresh = 0.4 * prepIntensityUnit
	midRainThresh = 2.5 * prepIntensityUnit
	heavyRainThresh = 10 * prepIntensityUnit
	lightSnowThresh = 0.13 * prepIntensityUnit
	midSnowThresh = 0.83 * prepIntensityUnit
	heavySnowThresh = 3.33 * prepIntensityUnit
	lightSleetThresh = 0.83 * prepIntensityUnit
	midSleetThresh = 3.33 * prepIntensityUnit
	heavySleetThresh = 6.67 * prepIntensityUnit

	lightWindThresh = 6.7056 * windUnit
	midWindThresh = 10 * windUnit
	heavyWindThresh = 17.8816 * windUnit

	lowHumidityThresh = 0.1
	highHumidityThresh = 0.9

	# Get key values from the hourObject
	precipIntensity = hourObject['precipIntensity']
	precipType = hourObject['precipType']
	vis = hourObject['visibility']
	cloudCover = hourObject['cloudCover']
	wind = hourObject['windSpeed']
	pop = hourObject['precipProbability']
	humidity = hourObject['humidity']


	possiblePrecip = ""
	cIcon = None
	cText = None
	# Add the possible precipitation text if pop is between 10-30% or if pop is greater than 10% but precipIntensity is between 0-0.02 mm/h
	if (pop >= 0.1 and pop < 0.3) or (
			pop >= 0.1 and precipIntensity > 0 and precipIntensity < (0.02 * prepIntensityUnit)):
		possiblePrecip = "possible-"
	else:
		cIcon = precipType

	# If precipIntensity is greater than 0.02 mm/h and no type fallback to rain icon
	if precipType == "none" and precipIntensity >= (0.02 * prepIntensityUnit):
		cIcon = "rain"

	if ((precipIntensity > 0) & (precipType != None)):
		if precipType == 'rain':
			if precipIntensity < lightRainThresh:
				cText = [mode, possiblePrecip + "very-light-rain"]
				cCond = possiblePrecip + "very-light-rain"
			elif precipIntensity >= lightRainThresh and precipIntensity < midRainThresh:
				cText = [mode, possiblePrecip + "light-rain"]
				cCond = possiblePrecip + "light-rain"
			elif precipIntensity >= midRainThresh and precipIntensity < heavyRainThresh:
				cText = [mode, "medium-rain"]
				cCond = "medium-rain"
			else:
				cText = [mode, "heavy-rain"]
				cCond = "heavy-rain"
		elif precipType == 'snow':
			if precipIntensity < lightSnowThresh:
				cText = [mode, possiblePrecip + "very-light-snow"]
				cCond = possiblePrecip + "very-light-snow"
			elif precipIntensity >= lightSnowThresh and precipIntensity < midSnowThresh:
				cText = [mode, possiblePrecip + "light-snow"]
				cCond = possiblePrecip + "light-snow"
			elif precipIntensity >= midSnowThresh and precipIntensity < heavySnowThresh:
				cText = [mode, "medium-snow"]
				cCond = "medium-snow"
			else:
				cText = [mode, "heavy-snow"]
				cCond = "heavy-snow"
		elif precipType == 'sleet':
			if precipIntensity < lightSleetThresh:
				cText = [mode, possiblePrecip + "very-light-sleet"]
				cCond = possiblePrecip + "very-light-sleet"
			elif precipIntensity >= lightSleetThresh and precipIntensity < midSleetThresh:
				cText = [mode, possiblePrecip + "light-sleet"]
				cCond = possiblePrecip + "light-sleet"
			elif precipIntensity >= midSleetThresh and precipIntensity < heavySleetThresh:
				cText = [mode, "medium-sleet"]
				cCond = "medium-sleet"
			else:
				cText = [mode, "heavy-sleet"]
				cCond = "heavy-sleet"
		else:
			# Because soemtimes there's precipitation not no type use a generic precipitation summary
			if precipIntensity < lightRainThresh:
				cText = [mode, possiblePrecip + "very-light-precipitation"]
				cCond = possiblePrecip + "very-light-precipitation"
			elif precipIntensity >= lightRainThresh and precipIntensity < midRainThresh:
				cText = [mode, possiblePrecip + "light-precipitation"]
				cCond = possiblePrecip + "light-precipitation"
			elif precipIntensity >= midRainThresh and precipIntensity < heavyRainThresh:
				cText = [mode, "medium-precipitation"]
				cCond = "medium-precipitation"
			else:
				cText = [mode, "heavy-precipitation"]
				cCond = "heavy-precipitation"

	# If visibility < 1000m, show fog
	elif vis < visThresh:
		return [mode, "fog"], "fog"
	elif cloudCover > cloudThreshold:
		cText = [mode, "heavy-clouds"]
		cCond = "heavy-clouds"
		cIcon = calculate_sky_icon(cloudCover, isDayTime)
	elif cloudCover > partlyCloudyThreshold:
		cIcon = calculate_sky_icon(cloudCover, isDayTime)
		if cloudCover > mostlyCloudyThreshold:
			cText = [mode, "medium-clouds"]
			cCond = "medium-clouds"
		else:
			cText = [mode, "light-clouds"]
			cCond = "light-clouds"
	else:
		cIcon = calculate_sky_icon(cloudCover, isDayTime)
		if cloudCover > mostlyClearThreshold:
			cText = [mode, "very-light-clouds"]
			cCond = "very-light-clouds"
		else:
			cText = [mode, "clear"]


	# Add wind or humidity text
	if wind >= lightWindThresh:
		if precipIntensity < 0.02:
			# Show the wind text before the sky text
			if wind >= lightWindThresh:
				cIcon = 'wind'
				cText = [mode, ["and", "light-wind", cCond]]
			elif wind >= midWindThresh:
				cIcon = 'wind'
				cText = [mode, ["and", "medium-wind", cCond]]
			elif wind >= heavyWindThresh:
				cIcon = 'wind'
				cText = [mode, ["and", "heavy-wind", cCond]]
		else:
			# Show the precipitation text before the wind text
			if wind >= heavyWindThresh:
				cText = [mode, ["and", cCond, "heavy-wind"]]
			elif wind >= midWindThresh:
				cText = [mode, ["and", cCond, "medium-wind"]]
			elif wind >= lightWindThresh:
				cText = [mode, ["and", cCond, "light-wind"]]
	elif humidity < lowHumidityThresh:
		# Do not change the icon
		cText = [mode, ["and", "low-humidity", cCond]]
	elif humidity > highHumidityThresh:
		# Do not change the icon
		cText = [mode, ["and", "high-humidity", cCond]]




	# If we have a condition text but no icon then use the sky cover icon
	if cIcon is None and cText is not None:
		if vis < visThresh:
			cIcon = "fog"
		elif cloudCover > cloudThreshold:
			cIcon = calculate_sky_icon(cloudCover, isDayTime)
		elif cloudCover > partlyCloudyThreshold:
			cIcon = calculate_sky_icon(cloudCover, isDayTime)
		else:
			cIcon = calculate_sky_icon(cloudCover, isDayTime)
	return cText, cIcon

Which is then called in the main script like this:

        hourText, hourIcon = calculate_text(hourItem, prepIntensityUnit, visUnits, windUnit, isDay, mode="title")
        hourItem["summary"] = translation.translate(hourText)
        hourItem["icon"] = hourIcon

        hourList.append(hourItem)

I'm hoping to get it running on the dev endpoint later today for currently and hourly, which would be very cool to see! Daily and minutely are trickier, but it'll be the same sort of syntax.

One other thought I have is that I might try to convert the translations repo over to python. Copilot is pretty good at this sort of stuff, and if it's not too tricky, then it would improve the performance a bit. That being said, the current implementation is amazingly fast for a js-python bridge. and only adds ~0.2 seconds, so workable either way

@cloneofghosts
Copy link
Collaborator Author

Oh nice to see you got it working.

Yeah the day summary is slightly trickier since its from 4am-4am. You could use the values that you are now to calculate the summary but the function uses intensity instead of accumulation to calculate the precipitation text. Maybe you could use one of the daily ones to calculate it? Is precipIntensity on daily an average of the day or just when it rains?

For the minutely summary maybe you could do an average over the next hour and use that to calculate the summary/icon? While not ideal would be good for now until the text summaries get added.

@cloneofghosts
Copy link
Collaborator Author

@alexander0042 I see that its been setup in the hourly blocks on the dev endpoint. Noticed two things:

  1. When the summary is Partly Cloudy the clear icon is shown. in calculate_sky_icon you need to change mostlyCloudyThreshold to partlyCloudyThreshold.
  2. When you specify a language which doesn't exist (such as asd) the site returns an Internal Server error. In my code I check if Translation is None and if so set it to English do it doesn't crash.

@alexander0042
Copy link
Collaborator

Ok, it's live on the dev endpoint as 2.5.0! I'm curious how the text is looking if you poke around a bit, and as an added bonus, the lang query param is working now too, so translations work!

Next up (~Friday) is the hour summaries aka minutely, which should just be either the currently condition if there's no precipitation, or a reasonably well bounded set of precipitation options,

The daily ones are a fair bit more complex, since there's four different chunks of the day that it cares about. Since the infrastructure is already there, I think I'm just going to expand the same approach that currently calculates 0-12 and 4-4 to include their four segments:

  • morning: 04:00 (4am) to 12:00 (12pm)
  • afternoon: 12:00 (12pm) to 17:00 (5pm)
  • evening: 17:00 (5pm) to 22:00 (10pm)
  • night: 22:00 (10pm) to 04:00 (4am)

Then calculate the weather condition for each segment using the code we've got with logic for accumulations added (either averaged over the 6 hours for things like intensity, or max, which makes sense for things like wind), From there, group the conditions together where possible (eg. using "starting-continuing-until", "during", "until-starting-again"), and combine two of them to make a sentence. There will have to be some sort of prioritization to it, since if we get something like today:

  • morning: Rain
  • afternoon: Snow
  • evening: Cloudy
  • night: Clear

It would be four different conditions, but since only two can be used, precipitation should probably be prioritized like:

  • ["sentence", ["and", DAY_CONDITION_SUMMARY, DAY_CONDITION_SUMMARY]]
  • ["sentence", ["and", ["during", WEATHER_CONDITION, TIME_OF_DAY], ["during", WEATHER_CONDITION, TIME_OF_DAY]]]
  • ["sentence", ["and", ["during", RAIN, morning], ["during", SNOW, afternoon]]]

Similar sort of approach for the week summaries, but at least no new data needs to be calculated for that one

@alexander0042
Copy link
Collaborator

@alexander0042 I see that its been setup in the hourly blocks on the dev endpoint. Noticed two things:

1. When the summary is Partly Cloudy the clear icon is shown. in `calculate_sky_icon` you need to change `mostlyCloudyThreshold` to `partlyCloudyThreshold`.

2. When you specify a language which doesn't exist (such as asd) the site returns an Internal Server error. In my code I check if Translation is None and if so set it to English do it doesn't crash.

Two good catches here, and I quickly tossed them in as 2.5.0a!

@cloneofghosts
Copy link
Collaborator Author

@alexander0042 One other quick thing I noticed: Looks like the currently block wasn't setup? When I test with different languages its always in EN and shows the same summary as on live.

@alexander0042
Copy link
Collaborator

I'd accidentally put it behind a version = 2.0 flag, but no reason for that! Fixed in 2.5.0b

@cloneofghosts
Copy link
Collaborator Author

Another really small thing I noticed is that if precipitation probability is < 10% and there's any precipitation it shows the precipitation as the summary when it probably shouldn't? Not a huge rush on this but something I should point out.

@cloneofghosts
Copy link
Collaborator Author

cloneofghosts commented Nov 26, 2024

With the daily/weekly summaries you need to watch out to make sure you're not over-complicating them. Your example would probably come out to something like: Rain this morning and Snow starting this afternoon continuing until later this afternoon.

Also wanted to mention that the weekly summary includes a temperature summary and the hourly summary is the same as the daily summary.

@cloneofghosts
Copy link
Collaborator Author

@alexander0042 Was doing some testing of the translations last night and noticed a few things:

  1. The Humid text should come after condition text. So Drizzle and Humid instead of Humid and Drizzle
  2. Same goes for Dry but haven't encountered that one yet.
  3. For the wind text if the conditions are clear then it just says the wind text. I don't mind either way but might be good for consistency.
  4. The precipitation text should be shown before the wind text no matter the intensity. Currently 0.02 mm/h and below it shows the wind text first.
  5. The wind thresholds need to be reversed or there needs to be an upper bounds check for the light/medium wind. As it is now it will always say Breezy even if the wind speed is 70 km/h.

I can create a PR to fix the above issues if you'd like.

Some comments:

  1. I feel like the Dry threshold is a bit too low? I tested various places last night and didn't encounter that text at all. Maybe it needs to be raised to be 15/20% so it shows up occasionally?
  2. Same thing as Dry where I think Humid might be a bit too high? Maybe 95% and above works better?

I set the Breezy threshold to 15mph based on this NOAA Glossary and for the most part I think that's fine. There was one location last night where it seemed like it was going to be breezy for a few days though that may be an outlier? It might just need to be tested more to see if it needs changed.

cloneofghosts added a commit to Pirate-Weather/pirate-weather-code that referenced this issue Nov 29, 2024
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
enhancement New feature or request priority: high
Projects
Status: Todo
Development

Successfully merging a pull request may close this issue.

2 participants