-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from junah201/develop
V1.0.0
- Loading branch information
Showing
18 changed files
with
667 additions
and
459 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.pyc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
FROM python:3.9-slim-buster | ||
|
||
WORKDIR /app | ||
COPY . . | ||
|
||
COPY requirements.txt requirements.txt | ||
RUN pip3 install -r requirements.txt | ||
|
||
EXPOSE 8000 | ||
|
||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import os | ||
|
||
DATABASE_URL = os.environ.get("DATABASE_URL") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from sqlalchemy import create_engine | ||
from sqlalchemy.ext.declarative import declarative_base | ||
from sqlalchemy.orm import sessionmaker | ||
from app.common import config | ||
|
||
import pymysql | ||
|
||
pymysql.install_as_MySQLdb() | ||
|
||
engine = create_engine( | ||
config.DATABASE_URL | ||
) | ||
SessionLocal = sessionmaker( | ||
autocommit=False, | ||
autoflush=False, | ||
bind=engine | ||
) | ||
|
||
Base = declarative_base() | ||
|
||
|
||
def get_db(): | ||
db = SessionLocal() | ||
try: | ||
yield db | ||
finally: | ||
db.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from sqlalchemy import Column, String, Integer, DateTime, JSON, ForeignKey, BOOLEAN, func, VARCHAR, VARCHAR, DATE | ||
from sqlalchemy.orm import relationship | ||
from sqlalchemy.dialects.mysql import INTEGER | ||
from app.database.database import Base | ||
|
||
|
||
class Channel(Base): | ||
__tablename__ = "channel" | ||
|
||
id = Column( | ||
INTEGER(unsigned=True), | ||
primary_key=True, | ||
index=True, | ||
comment="채널 고유번호" | ||
) | ||
panel_title = Column( | ||
VARCHAR(255), | ||
nullable=False, | ||
default="Naver Cafe", | ||
comment="패널에 보일 제목" | ||
) | ||
cafe_name = Column( | ||
VARCHAR(255), | ||
nullable=False, | ||
default="", | ||
comment="카페 이름" | ||
) | ||
cafe_id = Column( | ||
VARCHAR(255), | ||
nullable=False, | ||
default="", | ||
comment="카페 고유번호" | ||
) | ||
cafe_menu_id = Column( | ||
VARCHAR(255), | ||
nullable=False, | ||
default="", | ||
comment="카페 메뉴 고유번호" | ||
) | ||
cafe_board_type = Column( | ||
VARCHAR(255), | ||
nullable=False, | ||
default="L", | ||
comment="카페 게시판 타입" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from pydantic import BaseModel, validator, EmailStr, constr | ||
from typing import Optional | ||
|
||
|
||
class ChannelCreate(BaseModel): | ||
id: int | ||
|
||
|
||
class ChannelUpdate(BaseModel): | ||
panel_title: str | ||
cafe_name: str | ||
cafe_id: str | ||
cafe_menu_id: str | ||
cafe_board_type: str | ||
|
||
|
||
class Channel(ChannelCreate, ChannelUpdate): | ||
class Config: | ||
orm_mode = True | ||
|
||
|
||
class Post(BaseModel): | ||
title: str | ||
link: str | ||
writer: str | ||
date: str | ||
|
||
|
||
class Board(BaseModel): | ||
cafe_name: str | ||
board_name: str | ||
cafe_id: str | ||
cafe_menu_id: Optional[str] = None | ||
cafe_board_type: str |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
from fastapi import FastAPI, Depends, HTTPException | ||
from fastapi.middleware.cors import CORSMiddleware | ||
from app.database.database import get_db, engine | ||
from app.database import models, schemas | ||
from sqlalchemy.orm import Session | ||
import datetime | ||
from typing import Optional, List | ||
import aiohttp | ||
from bs4 import BeautifulSoup | ||
|
||
models.Base.metadata.create_all(bind=engine, checkfirst=True) | ||
|
||
app = FastAPI() | ||
|
||
app.add_middleware( | ||
CORSMiddleware, | ||
allow_origins=["*"], | ||
allow_credentials=True, | ||
allow_methods=["*"], | ||
allow_headers=["*"], | ||
) | ||
|
||
|
||
@app.get("/") | ||
async def root(): | ||
return f"Notification API (UTC: {datetime.datetime.utcnow().strftime('%Y.%m.%d %H:%M:%S')})" | ||
|
||
|
||
@app.get("/{id}/config", response_model=schemas.Channel) | ||
async def get_config(id: str, db: Session = Depends(get_db)): | ||
db_channel: Optional[models.Channel] = db.query(models.Channel).filter( | ||
models.Channel.id == id).first() | ||
|
||
if not db_channel: | ||
db_channel = models.Channel(id=id) | ||
db.add(db_channel) | ||
db.commit() | ||
|
||
return db_channel | ||
|
||
|
||
@app.post("/{id}/config", response_model=schemas.Channel) | ||
async def post_config(id: str, channel: schemas.ChannelUpdate, db: Session = Depends(get_db)): | ||
db_channel: Optional[models.Channel] = db.query(models.Channel).filter( | ||
models.Channel.id == id).first() | ||
|
||
if not db_channel: | ||
db_channel = models.Channel(id=id) | ||
db.add(db_channel) | ||
db.commit() | ||
|
||
db_channel.panel_title = channel.panel_title | ||
db_channel.cafe_name = channel.cafe_name | ||
db_channel.cafe_id = channel.cafe_id | ||
db_channel.cafe_menu_id = channel.cafe_menu_id | ||
db_channel.cafe_board_type = channel.cafe_board_type | ||
|
||
db.commit() | ||
|
||
return db_channel | ||
|
||
|
||
@app.get("/{id}/posts", response_model=List[schemas.Post]) | ||
async def get_posts(id: str, db: Session = Depends(get_db)): | ||
db_channel: Optional[models.Channel] = db.query(models.Channel).filter( | ||
models.Channel.id == id).first() | ||
|
||
if not db_channel: | ||
raise HTTPException( | ||
status_code=404, detail="Channel not found" | ||
) | ||
|
||
result: List[schemas.Post] = [] | ||
|
||
async with aiohttp.ClientSession( | ||
connector=aiohttp.TCPConnector(), | ||
headers={ | ||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36', }, | ||
trust_env=True | ||
) as session: | ||
async with session.get( | ||
"https://cafe.naver.com/ArticleList.nhn", | ||
params={ | ||
'search.clubid': db_channel.cafe_id, | ||
'search.menuid': db_channel.cafe_menu_id, | ||
'search.boardtype': db_channel.cafe_board_type, | ||
}, | ||
) as response: | ||
html = await response.text() | ||
|
||
soup = BeautifulSoup(html, 'html.parser') | ||
soup.prettify() | ||
|
||
table_els = soup.select("#main-area > div.article-board.m-tcol-c") | ||
els = table_els[1].select( | ||
"div.article-board > table > tbody > tr") | ||
|
||
def get_title(text: str): | ||
text = text.replace("\n", "").replace( | ||
"\t", "").replace(" ", "") | ||
return text | ||
|
||
for i in els: | ||
result.append( | ||
schemas.Post( | ||
title=get_title(i.select_one( | ||
"td.td_article > div.board-list > div > a.article").text), | ||
link=f"https://cafe.naver.com{i.select_one('td.td_article > div.board-list > div > a.article').get('href')}", | ||
writer=get_title(i.select_one( | ||
'td.td_name > div.pers_nick_area').text), | ||
date=f"{datetime.datetime.now().strftime('%Y.%m.%d')} {i.select_one('td.td_date').text}" | ||
) | ||
) | ||
|
||
return result | ||
|
||
|
||
@app.get("/{cafe_name}/boards", response_model=List[schemas.Board]) | ||
async def get_boards(cafe_name: str): | ||
result: List[schemas.Board] = [] | ||
|
||
async with aiohttp.ClientSession( | ||
connector=aiohttp.TCPConnector(), | ||
headers={ | ||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36', }, | ||
trust_env=True | ||
) as session: | ||
async with session.get( | ||
f"https://cafe.naver.com/{cafe_name}", | ||
) as response: | ||
html = await response.text() | ||
soup = BeautifulSoup(html, 'html.parser') | ||
soup.prettify() | ||
|
||
els = soup.select( | ||
"ul.cafe-menu-list > li" | ||
) | ||
|
||
for i in els: | ||
cafe_menu_id = None | ||
print(i.select_one("a").get("href").split("menuid=")) | ||
print(len(i.select_one("a").get("href").split("menuid="))) | ||
if len(i.select_one("a").get("href").split("menuid=")) > 1: | ||
cafe_menu_id = i.select_one("a").get("href").split( | ||
"menuid=")[1].split("&")[0] | ||
|
||
cafe_board_type = None | ||
if len(i.select_one("a").get("href").split("boardtype=")) > 1: | ||
cafe_board_type = i.select_one("a").get("href").split( | ||
"boardtype=")[1].split("&")[0] | ||
else: | ||
continue | ||
|
||
def get_title(text: str): | ||
text = text.replace("\n", "").replace( | ||
"\t", "").replace(" ", "").replace(" ", "").strip() | ||
return text | ||
|
||
result.append( | ||
schemas.Board( | ||
cafe_name=cafe_name, | ||
board_name=get_title(i.select_one("a").text), | ||
cafe_id=i.select_one("a").get("href").split( | ||
"clubid=")[1].split("&")[0], | ||
cafe_menu_id=cafe_menu_id, | ||
cafe_board_type=i.select_one("a").get("href").split( | ||
"boardtype=")[1].split("&")[0], | ||
) | ||
) | ||
|
||
return result |
Oops, something went wrong.