Skip to content

Commit

Permalink
init: Basic GUI setup + upgrades (#5)
Browse files Browse the repository at this point in the history
* init: Basic GUI setup + upgrades

* feat: workflows

* Use classes and improve everything

* More work

* Add ruff, fix docs, improve tests

* Final fixes

@Futura-Py/reviewers I have finished updating the code to follow PEP standards closer and for better readability, we able to get this pushed to main?

* Pull main

* Implement rdbende's suggestions

---------

Co-authored-by: Moosems <95927277+Moosems@users.noreply.github.com>
  • Loading branch information
im-coder-lg and Moosems authored May 6, 2023
1 parent e9879c9 commit 93b7b7a
Show file tree
Hide file tree
Showing 9 changed files with 268 additions and 151 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/formatting.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Code Formatting

on:
schedule:
- cron: 30 05 15 * *
workflow_dispatch:


jobs:
formatting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.8
- uses: actions/setup-node@v2
with:
node-version: '14'
- uses: extractions/setup-just@v1
with:
just-version: 0.8 # optional semver specification, otherwise latest
- name: Code Formatting (App)
run: |
just format
- name: Code Formatting (website)
run: |
npm i -g prettier
cd docs
npx prettier --write .
- uses: fregante/setup-git-user@v1
- name: Commit
run: |
git add .
git commit -m "style: Code Formatting Workflow" -m "Either triggered by cron or workflow_dispatch"
git push --force
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,9 @@ dmypy.json

# Pyre type checker
.pyre/

# Ruff
.ruff-cache/

# Mac stuff
.DS_Store
19 changes: 18 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
ruff:
ruff . --fix

isort:
python3 -m isort *.py
python3 -m isort tests/*.py
Expand All @@ -8,9 +11,23 @@ black:

install:
python3 -m pip install -r requirements.txt
pip3 install autopep8
ruff --version

run:
python3 main.py

run-tests:
python3 -m unittest tests/test_*.py
ruff .
python3 -m unittest tests/test_*.py

autopep8:
python3 -m autopep8 --in-place *.py
python3 -m autopep8 --in-place tests/test_*.py


format:
just black
just autopep8
just isort
ruff . --fix
75 changes: 73 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,73 @@
# Futura Weather
Coming very soon
<h1 align="center">Future Weather</h1>

A simple weather app that uses the OpenWeatherMap API to get the weather for a given city.

## Installation

To run this app, you will need Python 3 installed on your computer. Clone this repository and navigate to the directory containing the `weather_app.py` file.

Use the following command to install the required packages:
```bash
pip install -r requirements.txt
```


Once the packages are installed, you can run the app using the following command:

```bash
python3 weather_app.py
```
or
```bash
py3 weather_app.py
```


## Usage

When you run the app, a window will open with a search bar, a label to display the weather information, and two buttons.

To search for the weather in a city, simply enter the name of the city in the search bar and click the "Search for City" button. The app will display the temperature in Celsius for that city in the label.

To exit the app, click the "Exit" button.

# Documentation

Additionally, the code contains several functions, methods, and classes:

### `self_return_decorator()`
A decorator function that allows for chaining of methods. This function takes a method as an argument and returns a new function that calls the original method and returns the instance of the class.

### `App()`
A class that inherits from the Tk class and defines the main window of the app. The __init__ method sets up the menubar, window, and widgets. The about, resize_app, exit_app, and OWMCITY methods define the behavior of the corresponding buttons in the app.

### `.about()`
A method that displays a message box with information about the app.

### `.resize_app()`
A method that uses tkinter to detect the minimum size of the app, get the center of the screen, and place the app there.

### `.exit_app()`
A method that exits the app.

### `.OWMCITY()`
A method that sends a request to the OpenWeatherMap API to get the weather for a given city and displays the temperature in Celsius in the label. If the city is not found, the label displays an error message.

# Final Notes

## Contributing

Contributions are welcome! If you find a bug or have an idea for a new feature, please open an issue or submit a pull request.
## Pre-Commit Actions

Before making any commits, run the following command:

```bash
black .; isort .; ruff . --fix
```

This will automatically format the code to follow the PEP 8 style guide and run several linters to catch any errors or warnings

## License

This project is licensed under the MIT License. See the LICENSE file for details.
237 changes: 116 additions & 121 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,121 +1,116 @@
import os
import sys
from time import sleep

import requests

TOKEN = "c439e1209216cc7e7c73a3a8d1d12bfd"
BASE_URL = "https://api.openweathermap.org/data/2.5/weather?"
BREAK = "\n\n=====\n\n"
# TOKEN = os.getenv("TOKEN")


def breakliner():
print(f"{BREAK:-^30}")
return


def clearScreen():
if sys.platform == "windows":
os.system("cls")
else:
os.system("clear")
return


def exitQuery():
exitQ = input("Exit? ")
if exitQ == "y":
exit()
elif exitQ == "n":
clearScreen()
OWMCITY()

def data_processing():
main = data["main"]
humidity = main["humidity"]
pressure = main["pressure"]
temp = main["temp"]
temp_min = main["temp_min"]
temp_max = main["temp_max"]

def ktc(temp, temp_min, temp_max):
temp = temp - 273.15
temp_min = temp_min - 273.15
temp_max = temp_max - 273.15
print("Temperature: ", temp, "°C")
print("Minimum Temperature: ", temp_min, "°C")
print("Maximum Temperature: ", temp_max, "°C")

CITY = data["name"]
sys = data["sys"]
country = sys["country"]
CITY2 = CITY + "," + " " + country
if CITY == CITY2:
pass
else:
CITY = data["name"]
sys = data["sys"]
country = sys["country"]
CITY2 = CITY + "," + " " + country

w_main = data["weather"][0]["main"]
w_desc = data["weather"][0]["description"]
pressure = main["pressure"]
visibility = data["visibility"]
visibility_new = visibility / 1000
wind = data["wind"]["speed"]

def printData():
breakliner()
print(f"{CITY2:-^30}")
print(f"Weather: {w_main}:- {w_desc}")
ktc(temp, temp_min, temp_max)
print(f"Humidity: {humidity}%")
print(f"Pressure: {pressure} hPa")
print(f"Wind speed: {wind} m/s")
print(f"Visibility: {visibility} m (or) {visibility_new} km")

printData()

def OWMCITY():
# City Name request
global CITY
CITY = input(
"Enter City Name (should be in compliance to OpenWeatherMap's City Index): "
)
if CITY == "exit":
clearScreen()
print("Three")
sleep(1)
clearScreen()
print("Two")
sleep(1)
clearScreen()
print("One.")
sleep(1)
clearScreen()
print("Exiting...")
sleep(0.5)
exit()

else:
pass
# The URL in actuality be like:
URL = BASE_URL + "q=" + CITY + "&appid=" + TOKEN

# PROD: Request Response
global resp_PROD
resp_PROD = requests.get(URL)
if resp_PROD.status_code == 200:
pass
else:
raise TypeError("Oops, wrong city name or code?")

global data
data = resp_PROD.json()

data_processing()
exitQuery()

OWMCITY()
from __future__ import annotations

from platform import system
from tkinter import Menu, Tk, messagebox
from tkinter.ttk import Button, Entry, Frame, Label

from requests import Response
from requests import get as requests_get


class App(Tk):
def __init__(self):
super().__init__()
self.withdraw()

# Set up Menubar
if system() == "Darwin":
self.menubar = Menu(self)
# Apple menus have special names and special commands
self.app_menu = Menu(self.menubar, tearoff=0, name="apple")
self.menubar.add_cascade(label="App", menu=self.app_menu)
else:
self.menubar = Menu(self)
self.app_menu = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label="App", menu=self.app_menu)
self.menubar.add_command(label="About Weather", command=self.about)
self.config(menu=self.menubar)

# Set up window
self.title("Weather")
self.resizable(False, False)
self.configure(bg="white")

# Set up widgets
self.main_frame = Frame(self, padding=10)
self.main_frame.pack()

heading = Label(self.main_frame, text="Weather", font="Helvetica 13")
heading.grid(row=0, column=0, columnspan=2, padx=10, pady=10)

self.searchbar = Entry(self.main_frame, width=42)
self.searchbar.grid(row=1, column=0, columnspan=2, padx=10, pady=10)

self.label = Label(self.main_frame, text="", font=("Helvetica 13"))
self.label.grid(row=2, column=0, columnspan=2)

Button(self.main_frame, text="Search for City", command=self.OWMCITY).grid(
row=3, column=0, padx=10, pady=10
)
Button(self.main_frame, text="Exit", command=self.exit_app).grid(
row=3, column=1, padx=10, pady=10
)

self.resize_app()
self.deiconify()

def about(self) -> App:
"""Display a messagebox with information about the app."""
messagebox.showinfo(
"About Weather",
"Weather is a simple weather app that uses the OpenWeatherMap API to get the weather for a given city.",
parent=self,
)
return self

def resize_app(self) -> App:
"""Use tkinter to detect the minimum size of the app, get the center of the screen, and place the app there."""
# Update widgets so minimum size is accurate
self.update_idletasks()

# Get minimum size
minimum_width: int = self.winfo_reqwidth()
minimum_height: int = self.winfo_reqheight()

# Get center of screen based on minimum size
x_coords = int(self.winfo_screenwidth() / 2 - minimum_width / 2)
y_coords = int(self.winfo_screenheight() / 2 - minimum_height / 2) - 20
# `-20` should deal with Dock on macOS and looks good on other OS's

# Place app and make the minimum size the actual minimum size (non-infringable)
self.geometry(f"{minimum_width}x{minimum_height}+{x_coords}+{y_coords}")
self.wm_minsize(minimum_width, minimum_height)
return self

def exit_app(self) -> None:
"""Exit the app."""
self.destroy()

def OWMCITY(self) -> App:
"""Get the weather for a given city using the OpenWeatherMap API and display it in a label."""
# Get API key
api_key: str = "c439e1209216cc7e7c73a3a8d1d12bfd"

# Get city name
city: str = self.searchbar.get()

# Send request to OpenWeatherMap API
response: Response = requests_get(
f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}"
)
if response.status_code != 200:
self.label.configure(text="City not found")
return

# Get temperature in Celsius
temperature_kelvin: float = response.json()["main"]["temp"]
temperature_celsius = temperature_kelvin - 273.15

# Put in label
self.label.configure(text=f"{temperature_celsius:.2f}°C")
return self


if __name__ == "__main__":
app = App()
app.mainloop()
Loading

0 comments on commit 93b7b7a

Please sign in to comment.