diff --git a/Board.py b/Board.py new file mode 100644 index 0000000..346330e --- /dev/null +++ b/Board.py @@ -0,0 +1,206 @@ +import os +import sys +from User import User +from Post import Post + +SAVE_FILE_PATH = 'save.csv' + +class PostNotFoundError(Exception): + pass + +def autosave(func): + def decorated(caller, *args, **kwargs): + func(caller, *args, **kwargs) + caller.save() + return decorated + +def auto_check_authority(func): + def decorated(caller, *args, **kwargs): + article = func(caller, *args, **kwargs) + caller.check_authorization(article) + return article + return decorated + +class Board(): + POSTS_PER_PAGE = 15 + + def __init__(self): + self.current_page = 1 + self.posts = list() + try: + self.load() + except FileNotFoundError: + pass + try: + User.load() + except FileNotFoundError: + pass + self.login_actions = [ + { + 'title' : '로그인하기', + 'handler' : self.login + }, + { + 'title' : '회원가입하기', + 'handler' : self.create_user + } + ] + self.actions = [ + { + 'title': '작성하기', + 'handler': self.create_post + }, + { + 'title': '수정하기', + 'handler': self.update_post + }, + { + 'title': '삭제하기', + 'handler': self.delete_post + }, + { + 'title': '이전 페이지로', + 'handler': self.go_prev_page + }, + { + 'title': '다음 페이지로', + 'handler': self.go_next_page + } + ] + + def print_actions(self): + print('-' * 100) + for index, action in enumerate(self.actions): + print('{}. {}'.format(index + 1, action.get('title'))) + print('-' * 100) + + def get_user_action(self): + try: + user_action_index = int(input('> ')) + self.actions[user_action_index - 1]['handler']() + except (IndexError, ValueError): + input('잘못된 행동 번호를 입력하셨습니다.') + + def search_post_by_id(self, pid): + for index, post in enumerate(self.posts): + if post.has_pid(pid) and post.is_undeleted(): + return index, post + raise PostNotFoundError + + def get_undeleted_posts(self, post): + if post.is_undeleted(): + self.undeleted_posts.append(post) + + def list_posts(self): + self.undeleted_posts = list() + for post in self.posts: + self.get_undeleted_posts(post) + + if len(self.undeleted_posts) == 0: + print('게시판에 아직 작성된 글이 없습니다.') + print('한 번 작성해보는 것은 어떨까요?') + return + + page_from = (self.current_page - 1) * Board.POSTS_PER_PAGE + page_to = self.current_page * Board.POSTS_PER_PAGE + for post in self.undeleted_posts[page_from : page_to]: + print(post) + + @autosave + def create_post(self): + title = input('작성 > ') + self.posts.insert(0, Post(title, self.user.user_id, 'UNDELETED')) + + def check_authorization(self, post): + self.post_authorization = self.user.user_id == post.user_id + + @auto_check_authority + def get_updated_post(self): + try: + pid = input('몇 번 글을 수정할까요? ') + _, post = self.search_post_by_id(pid) + return post + except (PostNotFoundError, IndexError): + input('잘못된 글 번호를 입력하셨습니다.') + pass + + @autosave + def update_post(self): + post = self.get_updated_post() + + if not self.post_authorization: + input('{}님은 글 수정 권한이 없습니다.'.format(self.user.user_id)) + return + + try: + new_title = input('수정 > ') + post.update(new_title) + except (PostNotFoundError, IndexError, ValueError): #indexerror를 만드신 이유가 있을까 + input('잘못된 글 번호를 입력하셨습니다.') + + @auto_check_authority + def get_deleted_post(self): + try: + pid = input('몇 번 글을 삭제할까요?') + _, post = self.search_post_by_id(pid) + except (PostNotFoundError, IndexError): + input('잘못된 글 번호를 입력하셨습니다.') + return post + + @autosave + def delete_post(self): + post = self.get_deleted_post() + + if not self.post_authorization: + input('{}님은 글 삭제 권한이 없습니다.'.format(self.user.user_id)) + return + + try: + post.delete() + except (PostNotFoundError, IndexError, ValueError): + input('잘못된 글 번호를 입력하셨습니다.') + + def load(self): + global SAVE_FILE_PATH + f = open(SAVE_FILE_PATH, 'r', encoding='utf-8') + posts = f.readlines() + + for post in posts: + self.posts.append(Post.from_csv(post)) + f.close() + + def save(self): + global SAVE_FILE_PATH + f = open(SAVE_FILE_PATH, 'w', encoding='utf-8') + for post in self.posts: + print(post.to_csv(), file=f) + f.close() + + def go_prev_page(self): + if self.current_page == 1: + return + + self.current_page -= 1 + + def go_next_page(self): + if self.current_page * Board.POSTS_PER_PAGE < len(self.posts): + self.current_page += 1 + + def print_login_actions(self): + print('-' * 100) + for index, action in enumerate(self.login_actions): + print('{}. {}'.format(index + 1, action.get('title'))) + print('-' * 100) + + def get_user_login_action(self): + try: + login_action_index = int(input('> ')) + self.login_actions[login_action_index - 1]['handler']() + except (IndexError, ValueError): + input('잘못된 행동 번호를 입력하셨습니다.') + + def login(self): + self.user = User.login() + + def create_user(self): + self.user = User.create_user() diff --git a/Post.py b/Post.py new file mode 100644 index 0000000..96a64c1 --- /dev/null +++ b/Post.py @@ -0,0 +1,35 @@ +import datetime + +class Post(): + pid = 1 + + def __init__(self, title, user_id, deleted_at, pid=None, created_at=None): + self.pid = str(Post.pid) if pid is None else pid + self.title = title + self.user_id = user_id + self.deleted_at = deleted_at + self.created_at = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") if created_at is None else created_at + Post.pid += 1 + + def __str__(self): + return '{:^8} | {:60.60} | {} | {}'.format(self.pid, self.title, self.user_id, self.created_at) + + def update(self, new): + self.title = new + + def delete(self): + self.deleted_at = 'DELETE' + + def is_undeleted(self): + return self.deleted_at == 'UNDELETED' + + def has_pid(self, pid): + return self.pid == pid + + @classmethod + def from_csv(cls, csv): + pid, title, user_id, created_at, deleted_at = csv[:-1].split(',') + return cls(title, user_id, deleted_at, pid, created_at) + + def to_csv(self): + return '{},{},{},{},{}'.format(self.pid, self.title, self.user_id, self.created_at, self.deleted_at) diff --git a/User.py b/User.py new file mode 100644 index 0000000..fb15d57 --- /dev/null +++ b/User.py @@ -0,0 +1,94 @@ +import hashlib +import os +import sys + +def clear_screen(): + os.system('cls' if os.name == 'nt' else 'clear') + +def get_hashed_password(password): + hashed_password = hashlib.sha256(password.encode()).hexdigest() + return hashed_password + +class UserNotFoundError(Exception): + pass + +SAVE_FILE_PATH = 'User.csv' + +class User: + users = {} + login_success = False + + def __init__(self, user_id): + self.user_id = user_id + + @classmethod + def is_success(cls, user_id, user_pw): + if cls.users[user_id] == user_pw: + input('로그인에 성공하였습니다.') + return True + + @classmethod + def check_existence_of_user_id(cls, user_id): + try: + cls.users[user_id] + User.user_id_existence = True + except (KeyError): + print('아이디가 없습니다. 다시 입력해주세요.') + + @classmethod + def get_user_id_and_pw(cls): + User.user_id_existence = False + while not User.user_id_existence: + user_id = input('아이디 입력>') + cls.check_existence_of_user_id(user_id) + user_pw = get_hashed_password(input('비밀번호를 입력하세요>')) + return (user_id, user_pw) + + @classmethod + def login(cls): + user_id, user_pw = cls.get_user_id_and_pw() + User.login_success = cls.is_success(user_id, user_pw) + while not User.login_success: + input('비밀번호가 틀렸습니다. 엔터를 누르고 다시 입력해주세요.') + user_pw = get_hashed_password(input('비밀번호를 입력하세요>')) + User.login_success = cls.is_success(user_id, user_pw) + return User(user_id) + + @classmethod + def save(cls): + global SAVE_FILE_PATH + f = open(SAVE_FILE_PATH, 'w', encoding='utf-8') + for user_id, user_pw in cls.users.items(): + print(cls.to_csv(user_id,user_pw), file = f) + f.close() + + @classmethod + def to_csv(cls, user_id, user_pw): + return '{},{}'.format(user_id, user_pw) + + @classmethod + def load(cls): + global SAVE_FILE_PATH + f = open(SAVE_FILE_PATH, 'r', encoding = 'utf-8') + users = f.readlines() + + for user in users: + user_id, user_pw = cls.from_csv(user) + cls.users[user_id] = user_pw + + @classmethod + def create_user(cls): + new_user_id = input('아이디 작성>') + if new_user_id in User.users: + input('이미 존재하는 ID입니다.') + return + new_user_pw = get_hashed_password(input('비밀번호를 입력하세요>')) + cls.users[new_user_id] = new_user_pw + cls.save() + return User(new_user_id) + + @classmethod + def from_csv(cls, csv): + user_id, user_pw = csv[:-1].split(',') + return (user_id, user_pw) + diff --git a/main.py b/main.py new file mode 100644 index 0000000..6530686 --- /dev/null +++ b/main.py @@ -0,0 +1,33 @@ +import os +import sys +from User import User +from Post import Post +from Board import Board + +def clear_screen(): + os.system('cls' if os.name == 'nt' else 'clear') + +def print_greeting_message(): + clear_screen() + greeting_message = ''' + 본 프로그램은 광일공방에서 중고거래 사이트를 기웃기웃 거리던 광일이가 + 어느날 삘이 꽂혀 '아! 여기가 돈 나오는 방석이다!' 생각하자마자 아주 + 급하게 만들어낸 불안정한 중고거래 사이트입니다. 물론 아직은 허접한 + 게시판이지만 1000만 사용자를 꿈꾸는 광일이는 커다란 희망의 씨앗을 마음에 + 품고서 오늘도 밤을 지새우며 코드를 작성합니다. + ''' + print(greeting_message) + input('프로그램을 시작하시려면 엔터키를 입력하세요...') + +if __name__ == "__main__": + print_greeting_message() + board = Board() + while not User.login_success: + clear_screen() + board.print_login_actions() + board.get_user_login_action() + while True: + clear_screen() + board.list_posts() + board.print_actions() + board.get_user_action() \ No newline at end of file