Skip to content

Commit

Permalink
feature/events widget (#1)
Browse files Browse the repository at this point in the history
* added events widget and a redesign of the app

* small restructuring of the codebase and test changes

* changed poster image in README.md
  • Loading branch information
SerhiiStets authored May 9, 2023
1 parent 4d17e9b commit a44a01e
Show file tree
Hide file tree
Showing 17 changed files with 384 additions and 180 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Skylab is a text user interface (TUI) tool that displays upcoming space launches in a user-friendly way.

![skylab](https://i.imgur.com/96seWOP.png)
![skylab](https://i.imgur.com/Hopa3mN.png)

Skylab is built using the [Textual](https://github.com/Textualize/textual) framework.

Expand Down
28 changes: 14 additions & 14 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "skylab"
version = "0.1.2"
version = "0.2.1"
description = "A TUI for showing latest upcoming rocket launches."
authors = ["SerhiiStets <[email protected]>"]
license = "MIT"
Expand All @@ -10,7 +10,7 @@ repository="https://github.com/SerhiiStets/skylab"
[tool.poetry.dependencies]
python = "^3.9"
pytest = "^7.3.1"
textual = {extras = ["dev"], version = "^0.19.1"}
textual = {version = "^0.19.1", extras = ["dev"]}
pydantic = "^1.10.7"
tzlocal = "^4.3"
requests = "^2.28.2"
Expand Down
87 changes: 53 additions & 34 deletions skylab/api.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,58 @@
"""Api module for receiving data from thespacedevs."""

import datetime

import requests

from skylab.models import Launch

GET_UPCOMING_LAUNCHES = "https://ll.thespacedevs.com/2.2.0/launch/upcoming/"


def get_upcoming_laucnhes(url: str, timeout: float = 5.0) -> dict:
"""Get request for upcoming launches."""
try:
response = requests.get(url, timeout=timeout)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as ex:
raise ValueError(f"Failed to retrieve upcoming launches: {ex}.")


def launch_factory() -> list[Launch]:
"""Makes a request for upcoming launches, modifies and validates them."""
results = get_upcoming_laucnhes(GET_UPCOMING_LAUNCHES)["results"]
launches = []
for launch_dict in results:
# checking if the launch date is not expired
net = datetime.datetime.fromisoformat(launch_dict["net"][:-1])
if net < datetime.datetime.now():
continue

# removing "configuration" nested layer from given data
rocket_config = launch_dict["rocket"].pop("configuration")
launch_dict["rocket"].update(rocket_config)

# getting address name from "location" json
launch_pad_address = launch_dict["pad"].pop("location")["name"]
launch_dict["pad"]["address"] = launch_pad_address
launches.append(Launch(**launch_dict))
return launches
from skylab.models.event_models import Event
from skylab.models.launch_models import Launch


class SpaceDevApi:
"""Interface for interacting with SpaceDev API."""

BASE_URL = "https://ll.thespacedevs.com/2.2.0"
GET_UPCOMING_LAUNCHES = f"{BASE_URL}/launch/upcoming/"
GET_UPCOMING_EVENTS = f"{BASE_URL}/event/upcoming/"

def __init__(self, timeout: float = 5.0) -> None:
"""Initialize SpaceDevApi object."""
self.timeout = timeout
self.session = requests.Session()

def get_request(self, url: str) -> dict:
"""Make a get request to the SpaceDev API."""
try:
response = self.session.get(url, timeout=self.timeout)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as ex:
raise ValueError(f"Failed to retrieve the info: {ex}.")

def launch_factory(self) -> list[Launch]:
"""Makes a request for upcoming launches, modifies and validates them."""
results = self.get_request(self.GET_UPCOMING_LAUNCHES)["results"]
launches = []
for launch_dict in results:
# checking if the launch date is not expired
net = datetime.datetime.fromisoformat(launch_dict["net"][:-1])
if net < datetime.datetime.now():
continue

# removing "configuration" nested layer from given data
rocket_config = launch_dict["rocket"].pop("configuration")
launch_dict["rocket"].update(rocket_config)

# getting address name from "location" json
launch_pad_address = launch_dict["pad"].pop("location")["name"]
launch_dict["pad"]["address"] = launch_pad_address
launches.append(Launch(**launch_dict))
return launches

def event_factory(self) -> list[Event]:
"""Make a request for upcoming events, modifies and validates them."""
results = self.get_request(self.GET_UPCOMING_EVENTS)["results"]
events = []
for event_dict in results:
events.append(Event(**event_dict))
return events
66 changes: 56 additions & 10 deletions skylab/css/styles.css
Original file line number Diff line number Diff line change
@@ -1,28 +1,74 @@
.bg-text{
text-opacity: 70%;
}


.title{
text-style: underline;
}

#title-grid{
layout: grid;
grid-size: 2 1;
height: 1;
margin-left: 3;
margin-top: 1;
margin-bottom: 1;
grid-columns: 2fr 1fr;
grid-gutter:3;
}

#window-split {
layout: grid;
grid-size: 2 1;
grid-columns: 2fr 1fr;
}

LaunchWidget {
layout: grid;
grid-size: 2 1;
grid-gutter:2;
grid-columns: 2fr 1fr;
margin: 3;
grid-columns: 4fr 1fr;
margin-left: 2;
margin-right: 2;
margin-bottom: 2;
padding-left: 5;
padding-right: 5;
padding-top: 2;
padding-bottom: 2;
height: 16;
}

TimeDisplay {

#right-launch-widget{
width: 20;
grid-gutter: 1 4;
content-align: center middle;
text-opacity: 60%;
height: 3;
}

#watch_button{
dock:right;
height: 3;
#launch-counter{
content-align: center middle;
height: 3;
}

#watch-button{
align: center middle;
width: 100%;
}

.launch-time {
text-style: bold;
/* Event widgets */

EventWidget{
padding: 1;
margin-left: 2;
margin-right: 2;
margin-bottom: 2;
}

.event-buttons{
layout: grid;
grid-size: 2 1;
grid-columns: 1fr 1fr;
grid-gutter:2;
height: 3;
}
Empty file added skylab/models/__init__.py
Empty file.
15 changes: 15 additions & 0 deletions skylab/models/event_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""Event pydantic models."""

from typing import Union

from pydantic import BaseModel


class Event(BaseModel):
"""Event model from SpaceDev API."""

name: str
description: str = "No description yet."
date: Union[str, None]
news_url: Union[str, None]
video_url: Union[str, None]
10 changes: 10 additions & 0 deletions skylab/models.py → skylab/models/launch_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,34 @@


class LaunchProvier(BaseModel):
"""Provider information for the launch (i.e. SpaceX)."""

name: str


class Mission(BaseModel):
"""Mission information for the launch."""

name: str
description: str


class Rocket(BaseModel):
"""Rocket configuration for the launch."""

full_name: str


class LaunchPad(BaseModel):
"""Launch pad information for the launch."""

name: str
address: str


class Launch(BaseModel):
"""Launch model from SpaceDev API."""

name: str
net: str
pad: LaunchPad = Field(LaunchPad(name="Unknown", address="Unknown"))
Expand Down
9 changes: 9 additions & 0 deletions skylab/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""Skylab const and settings module."""

import os

import tzlocal

CURR_DIR = os.path.dirname(os.path.abspath(__file__))
LOCAL_TIMEZONE = tzlocal.get_localzone()
CSS_PATH = os.path.join(CURR_DIR, "css/styles.css")
Loading

0 comments on commit a44a01e

Please sign in to comment.