From 45edc12b003e8d568afbe4ba3cc4a55b703e822d Mon Sep 17 00:00:00 2001 From: jjj0611 <44603719+jjj0611@users.noreply.github.com> Date: Mon, 11 Feb 2019 20:39:02 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B4=91=EC=9D=BC=EA=B3=B5=EB=B0=A9=20?= =?UTF-8?q?=EC=A4=91=EA=B3=A0=EB=82=98=EB=9D=BC=20#4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BOARD.py | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ POST.py | 54 +++++++++++++++++++++++ SAVE.py | 8 ++++ USER.py | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 32 ++++++++++++++ 5 files changed, 352 insertions(+) create mode 100644 BOARD.py create mode 100644 POST.py create mode 100644 SAVE.py create mode 100644 USER.py create mode 100644 main.py diff --git a/BOARD.py b/BOARD.py new file mode 100644 index 0000000..7b57296 --- /dev/null +++ b/BOARD.py @@ -0,0 +1,131 @@ +import math +from POST import Post +from USER import User, UserManager +from SAVE import autosave, SAVE_PATH_POST + +class PostNotFoundError(Exception): + pass + +class Board: + POSTS_PER_PAGE = 15 + + def __init__(self): + self.posts = list() + self.no_of_pages = 1 + self.current_page_no = 1 + self.actions = [ + { + "title" : "작성하기", + "handler" : self.write_post + }, + { + "title" : "수정하기", + "handler" : self.update_post + }, + { + "title" : "삭제하기", + "handler" : self.delete_post + }, + { + "title" : "저장하기", + "handler" : self.save + }, + { + "title" : "이전페이지", + "handler" : self.move_previous_page + }, + { + "title" : "다음페이지", + "handler" : self.move_next_page + }] + try: + self.load() + except: + pass + + def save(self): + global SAVE_PATH_POST + f = open(SAVE_PATH_POST, 'w+') + for post in self.posts: + print(post.to_csv(), file = f) + f.close() + + def load(self): + global SAVE_PATH_POST + f = open(SAVE_PATH_POST, 'r') + posts = f.readlines() + for post in posts: + self.posts.append(Post.from_csv(post)) + f.close() + + def show_posts(self): + if self.posts == []: + print('게시판에 아직 작성된 글이 없습니다.') + print('한번 작성해보는 것은 어떨까요?') + print('-' * 100) + return + + self.posts_shown_list = list(filter(lambda x: x.post_shown_status == True, self.posts)) + # or self.posts_shown_list = [post for post in self.posts if post.post_shown_status ==True] + self.no_of_pages = math.ceil( len(self.posts_shown_list) / Board.POSTS_PER_PAGE ) + + page = [post for index, post in enumerate(self.posts_shown_list) if Board.POSTS_PER_PAGE * (self.current_page_no - 1) <= index < Board.POSTS_PER_PAGE * self.current_page_no] + for post in page: + print(post) + print('-' * 100) + + def search_post_by_post_no(self, post_no): + for post in self.posts: + if post.has_post_no(post_no): + return post + raise PostNotFoundError + + @autosave + def write_post(self): + post_title = input('작성 >') + self.posts.insert(0, Post(post_title, UserManager.LOGINNED_USER.user_id)) + self.current_page_no = 1 + + @autosave + def update_post(self): + try: + post_no = int(input('몇 번 글을 수정할까요?')) + post = self.search_post_by_post_no(post_no) + post.update_title() + except (PostNotFoundError, ValueError, IndexError): + print("잘못된 글 번호를 입력하셨습니다.") + + @autosave + def delete_post(self): + try: + post_no = int(input('몇 번 글을 삭제할까요?')) + post = self.search_post_by_post_no(post_no) + post.change_shown_status() + except (PostNotFoundError, ValueError, IndexError): + print("잘못된 글 번호를 입력하셨습니다.") + + def move_previous_page(self): + self.current_page_no -= 1 + if self.current_page_no < 1: + self.current_page_no = 1 + print("첫 페이지입니다.") + + def move_next_page(self): + self.current_page_no += 1 + if self.current_page_no > self.no_of_pages: + self.current_page_no = self.no_of_pages + print("마지막 페이지입니다.") + + def show_actions(self): + for index, action in enumerate(self.actions): + print("{}. {}".format(index+1, action["title"])) + print('-' * 100) + + def execute_action(self): + try: + action_no = int(input('>')) + self.actions[action_no - 1]["handler"]() + except (IndexError, ValueError): + print("잘못된 입력입니다.") + + input('엔터를 누르세요.') \ No newline at end of file diff --git a/POST.py b/POST.py new file mode 100644 index 0000000..12b73bc --- /dev/null +++ b/POST.py @@ -0,0 +1,54 @@ +import datetime +from USER import User, UserManager + +class NotAuthorizedError(Exception): + pass + +def check_authorization(action_name): + def wrapper(func): + def inner(caller, *args, **kwargs): + try: + if not caller.is_same_writer(): + raise NotAuthorizedError + func(caller, *args, **kwargs) + except NotAuthorizedError: + print("{}님께 {} 권한이 없습니다.".format(UserManager.LOGINNED_USER.user_id, action_name)) + return inner + return wrapper + +class Post: + COUNT = 0 + + def __init__(self, post_title, post_writer, post_no = None, post_time=None, post_shown_status=None): + Post.COUNT += 1 + self.post_no = Post.COUNT if post_no is None else post_no + self.post_title = post_title + self.post_writer = post_writer + self.post_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M') if post_time is None else post_time + self.post_shown_status = True if post_shown_status is None else post_shown_status + + def __str__(self): + return f"{self.post_no:^8} | {self.post_title:60.60} | {self.post_writer:^10.10} | {self.post_time}" + + @check_authorization("수정") + def update_title(self): + updated_title = input('수정>') + self.post_title = updated_title + + @check_authorization("삭제") + def change_shown_status(self): + self.post_shown_status = False + + def has_post_no(self, post_no): + return self.post_no == post_no and self.post_shown_status == True + + def is_same_writer(self): + return self.post_writer == UserManager.LOGINNED_USER.user_id + + @classmethod + def from_csv(cls, csv): + post_no, post_title, post_writer, post_time, post_shown_status = csv.strip('\n').split(',') + return cls(post_title, post_writer, post_no = int(post_no), post_time = post_time, post_shown_status = post_shown_status == "True") + + def to_csv(self): + return f"{self.post_no},{self.post_title},{self.post_writer},{self.post_time},{self.post_shown_status}" diff --git a/SAVE.py b/SAVE.py new file mode 100644 index 0000000..2f1b270 --- /dev/null +++ b/SAVE.py @@ -0,0 +1,8 @@ +SAVE_PATH_POST = "post.csv" +SAVE_PATH_USER = "user_info.csv" + +def autosave(func): + def wrapper(caller, *args, **kwargs): + func(caller) + caller.save() + return wrapper diff --git a/USER.py b/USER.py new file mode 100644 index 0000000..f08368e --- /dev/null +++ b/USER.py @@ -0,0 +1,127 @@ +import hashlib +from SAVE import SAVE_PATH_USER, autosave + +def get_hashed_password(password): + hashed_password = hashlib.sha256(password.encode()).hexdigest() + return hashed_password + +class ExistingIdError(Exception): + pass + +class NotExistingIdError(Exception): + pass + +class InvalidPassword(Exception): + pass + +class UserManager: + LOGINNED_USER = None + + def __init__(self): + self.users = list() + self.action_list = [ + { + "title" : "로그인", + "handler" : self.user_login + }, + { + "title" : "회원가입", + "handler" : self.user_join + } + ] + try: + self.load() + except FileNotFoundError: + pass + + def load(self): + global SAVE_PATH_USER + f = open(SAVE_PATH_USER, 'r') + users = f.readlines() + for user in users: + self.users.append(User.from_csv(user)) + f.close() + + def save(self): + global SAVE_PATH_USER + f = open(SAVE_PATH_USER, 'w') + for user in self.users: + print(user.to_csv(), file = f) + f.close() + + def search_user_by_id_for_login(self, user_id): + for user in self.users: + if user.has_id(user_id): + return user + raise NotExistingIdError + + def search_user_by_id_for_join(self, user_id): + for user in self.users: + if user.has_id(user_id): + raise ExistingIdError + + def user_login(self): + try: + user_id = input("ID를 입력하세요.") + user = self.search_user_by_id_for_login(user_id) + + hashed_password = get_hashed_password(input("password를 입력하세요.")) + if not user.check_password(hashed_password): + raise InvalidPassword + if user.check_password(hashed_password): + UserManager.LOGINNED_USER = user + print("로그인 성공 : 환영합니다 {}님".format(UserManager.LOGINNED_USER.user_id)) + except NotExistingIdError: + print("존재하지 않는 ID입니다.") + except InvalidPassword: + print("비밀번호가 일치하지 않습니다.") + + @autosave + def user_join(self): + try: + user_id = input("ID를 입력하세요.") + self.search_user_by_id_for_join(user_id) + + hashed_password = get_hashed_password(input("password를 입력하세요.")) + user = User(user_id, hashed_password) + self.users.append(user) + + UserManager.LOGINNED_USER = user + print("로그인 성공 : 환영합니다 {}님".format(user.user_id)) + except ExistingIdError: + print("이미 존재하는 ID입니다.") + + def print_actions(self): + print('-' * 100) + for index, action in enumerate(self.action_list): + print("{}. {}".format(index + 1, action["title"]) ) + print('-' * 100) + + def execute_action(self): + try: + action_no = int(input("번호를 입력하세요.")) + self.action_list[action_no - 1]["handler"]() + except (IndexError, ValueError): + print("잘못된 입력입니다.") + + input("엔터를 입력하세요.") + +class User: + + def __init__(self, user_id, user_password): + self.user_id = user_id + self.user_password = user_password + + @classmethod + def from_csv(cls, csv): + user_id, user_password = csv.strip('\n').split(',') + return cls(user_id, user_password) + + def to_csv(self): + return f"{self.user_id},{self.user_password}" + + def has_id(self, user_id): + return self.user_id == user_id + + def check_password(self, hashed_password): + return self.user_password == hashed_password diff --git a/main.py b/main.py new file mode 100644 index 0000000..b2038ff --- /dev/null +++ b/main.py @@ -0,0 +1,32 @@ +import os +from BOARD import Board +from POST import Post +from USER import UserManager, User + +def intro(): + greeting = ''' + 본 프로그램은 광일공방에서 중고거래 사이트를 기웃기웃 거리던 광일이가 + 어느날 삘이 꽂혀 '아! 여기가 돈 나오는 방석이다!' 생각하자마자 + 아주 급하게 만들어낸 불안정한 중고거래 사이트입니다. 물론 아직은 + 허접한 게시판이지만 1000만 사용자를 꿈꾸는 광일이는 커다란 희망의 씨앗을 + 마음에 품고서 오늘도 밤을 지새우며 코드를 작성합니다. + + 프로그램을 시작하려면 로그인하세요...''' + print(greeting, end = '') + input() + clear_screen() + +def clear_screen(): + os.system('cls' if os.name == 'nt' else 'clear') + +if __name__ == "__main__": + user_manager = UserManager() + while UserManager.LOGINNED_USER == None: + user_manager.print_actions() + user_manager.execute_action() + board = Board() + while True : + board.show_posts() + board.show_actions() + board.execute_action() + clear_screen() \ No newline at end of file