Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Docker Deployment #32

Merged
merged 5 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .flake8

This file was deleted.

35 changes: 17 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,24 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10"]
python-version: ["3.12"]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python
run: uv python install

- name: Install the project
run: uv sync --all-extras --dev

- name: Lint with ruff
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 api/ --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 api/ --count --exit-zero --max-complexity=10 --max-line-length=88 --statistics
uv run ruff check

- name: Test with pytest
run: |
pytest
uv run pytest tests
22 changes: 17 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
.vercel
venv
__pycache__/
test.py
# .env
.env
.env.local
.env.production

# rope
.ropeproject

# cache
.mypy_cache
.pytest_cache
.pytest_cache
__pycache__/
.ruff_cache

# vercel
.vercel

# virtual environments
.venv
venv
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.9
3.12
6 changes: 4 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"python.analysis.extraPaths": ["venv/lib"],
"python.linting.mypyEnabled": true,
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
"editor.defaultFormatter": "charliermarsh.ruff"
},
"[toml]": {
"editor.defaultFormatter": "tamasfe.even-better-toml"
}
}
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM python:3.12-slim

# Install uv.
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

# Copy the application into the container.
COPY . /app

# Install the application dependencies.
WORKDIR /app
RUN uv sync --frozen --no-cache

# Run the application.
CMD ["/app/.venv/bin/fastapi", "run", "app/main.py", "--port", "8000", "--host", "0.0.0.0"]
40 changes: 14 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

<h4>THIS SERVICE IS ONLY CREATED TO SATISFY THE NEED FOR AN API FOR [MYDRAMALIST.COM](https://mydramalist.com). THIS WILL BE STOPPED ONCE AN OFFICIAL API WILL BE RELEASED.</h4>

### Deploy Your Own
</div>

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2FTheBoringDude%2Fkuryana)
## Deployment

</div>
### [Vercel](https://github.com/tbdsux/kuryana/tree/deploy/vercel)

Make sure to set `deploy/vercel` as the branch.

## API Use

Expand Down Expand Up @@ -88,39 +90,25 @@ https://kuryana.vercel.app/dramalist/{user_id}

## Development

- Minimum Python Version : `3.9`,
- Minimum Python Version : `3.12`,

- Always make sure to create a virtualenvironment
- Make sure `uv` is installed in your machine, [more details](https://docs.astral.sh/uv/getting-started/installation/)

```sh
python -m venv venv
source ./venv/bin/activate.sh # change depends on your shell and os
```
- Sync project dependencies

- Install the dependencies
```sh
pip install -r requirements.txt
uv sync
```

### Dev Server

You can start the development server in two ways:

- Using the vercel CLI (`localhost:3000`)

```sh
vercel dev
```

- The api will be `http://localhost:3000/api/*`

- Or with `uvicorn` (`localhost:8000`)
Start development server.

```sh
uvicorn api.main:app --reload
```
```sh
uv run fastapi dev
```

- The api will be `http://localhost:8000/*`
[FastAPI CLI] (https://fastapi.tiangolo.com/fastapi-cli/)

## NOTE

Expand Down
File renamed without changes.
Empty file added app/handlers/__init__.py
Empty file.
27 changes: 14 additions & 13 deletions api/fetch.py → app/handlers/fetch.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from typing import Dict, Any, List

from api import MYDRAMALIST_WEBSITE
from api.parser import BaseFetch
import re
from typing import Any, Dict, List
from urllib.parse import urljoin

from bs4 import BeautifulSoup
from urllib.parse import urljoin
import re

from app import MYDRAMALIST_WEBSITE
from app.handlers.parser import BaseFetch


class FetchDrama(BaseFetch):
Expand Down Expand Up @@ -128,7 +128,7 @@ def _get_main_container(self) -> None:
_work_headers = [i.text.strip() for i in _works_container.find_all("h5")]
_work_tables = _works_container.find_all("table")

for j, k in zip(_work_headers, _work_tables):
for j, k in zip(_work_headers, _work_tables, strict=False):
# theaders = ['episodes' if i.text.strip() == '#' else i.text.strip() for i in k.find("thead").find_all("th")]
bare_works: List[Dict[str, Any]] = []

Expand Down Expand Up @@ -209,7 +209,7 @@ def _get_main_container(self) -> None:
__temp_cast_headers = __casts_container.find_all("h3")
__temp_cast_lists = __casts_container.find_all("ul")

for j, k in zip(__temp_cast_headers, __temp_cast_lists):
for j, k in zip(__temp_cast_headers, __temp_cast_lists, strict=False):
casts = []
for i in k.find_all("li"):
__temp_cast = i.find("a", class_="text-primary")
Expand Down Expand Up @@ -365,8 +365,8 @@ def _get_main_container(self) -> None:
dramas = [self._parse_drama(item) for item in container]
stats = [self._parse_total_stats(item) for item in container]

items = dict()
for title, drama, stat in zip(titles, dramas, stats):
items = {}
for title, drama, stat in zip(titles, dramas, stats, strict=False):
items[title] = {"items": drama, "stats": stat}

self.info["list"] = items
Expand All @@ -381,9 +381,9 @@ def _parse_total_stats(self, item: BeautifulSoup) -> Dict[str, str]:
movies_stats = item.find("label", class_="mdl-style-movies")
days_stats = item.find("label", class_="mdl-style-days")
return {
label.find("span", class_="name")
.get_text(strip=True): label.find("span", class_="cnt")
.get_text(strip=True)
label.find("span", class_="name").get_text(strip=True): label.find(
"span", class_="cnt"
).get_text(strip=True)
for label in [
drama_stats,
tvshows_stats,
Expand All @@ -405,6 +405,7 @@ def _parse_drama(self, item: BeautifulSoup) -> Dict[str, str]:
item_scores,
item_episode_seens,
item_episode_totals,
strict=False,
):
parsed_item = {
"name": name.get_text(strip=True),
Expand Down
8 changes: 4 additions & 4 deletions api/parser.py → app/handlers/parser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import datetime
from datetime import datetime, timezone
from typing import Any, Dict, List, Type, TypeVar, Union
from urllib.parse import urljoin

Expand All @@ -7,7 +7,7 @@
from bs4 import BeautifulSoup
from bs4.element import NavigableString, Tag

from api import MYDRAMALIST_WEBSITE
from app import MYDRAMALIST_WEBSITE

T = TypeVar("T", bound="Parser")

Expand Down Expand Up @@ -102,7 +102,7 @@ def search(self) -> Dict[str, Any]:
return {
"query": self.query,
"results": self.search_results,
"scrape_date": datetime.utcnow(),
"scrape_date": datetime.now(timezone.utc),
}


Expand All @@ -120,7 +120,7 @@ def fetch(self) -> Dict[str, Any]:
return {
"slug_query": self.query,
"data": self.info,
"scrape_date": datetime.utcnow(),
"scrape_date": datetime.now(timezone.utc),
}

def _get(self) -> None:
Expand Down
14 changes: 7 additions & 7 deletions api/search.py → app/handlers/search.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from typing import Any, Tuple, Union, Optional

from api import MYDRAMALIST_WEBSITE
from api.parser import BaseSearch
from typing import Any, Optional, Tuple, Union
from urllib.parse import urljoin

from bs4 import BeautifulSoup
from bs4.element import Tag, NavigableString, ResultSet
from urllib.parse import urljoin
from bs4.element import NavigableString, ResultSet, Tag

from app import MYDRAMALIST_WEBSITE
from app.handlers.parser import BaseSearch


class Search(BaseSearch):
Expand Down Expand Up @@ -108,7 +108,7 @@ def _get_search_results(self) -> None:

# extract drama title
r["title"] = title.strip()

# drama ranking
r["ranking"] = self._res_get_ranking(result)

Expand Down
10 changes: 4 additions & 6 deletions api/main.py → app/main.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
from typing import Dict, Any

from fastapi import FastAPI, Response
from fastapi.middleware.cors import CORSMiddleware
from typing import Any, Dict

# bypassing cloudflare anti-bot
import cloudscraper
from fastapi import FastAPI, Response
from fastapi.middleware.cors import CORSMiddleware

from api.utils import search_func, fetch_func

from app.utils import fetch_func, search_func

app = FastAPI()

Expand Down
16 changes: 8 additions & 8 deletions api/utils.py → app/utils.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
from typing import Dict, Any, Tuple
from typing import Any, Dict, Tuple

from api.search import Search
from api.fetch import (
from app.handlers.fetch import (
FetchCast,
FetchDrama,
FetchDramaList,
FetchList,
FetchPerson,
FetchCast,
FetchReviews,
FetchDramaList,
)
from app.handlers.search import Search


def error(code: int, description: str) -> Dict[str, Any]:
return {
"error": True,
"code": code,
"description": "404 Not Found"
if code == 404
else description, # prioritize error 404
"description": (
"404 Not Found" if code == 404 else description
), # prioritize error 404
}


Expand Down
7 changes: 7 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- 8000:8000
14 changes: 14 additions & 0 deletions dokploy.docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
services:
app:
build:
context: .
dockerfile: Dockerfile
restart: always
expose:
- 8000
networks:
- dokploy-network

networks:
dokploy-network:
external: true
Loading
Loading