월~토까지 C# 에 대해서 그룹스터디를 진행하는 과제를 부여받았습니다.

 

녹록치 않은 멤버들이라 터프한 스터디 일정이 잡혔습니다.

 

분명 많은 분량이지만 아주 못따라갈 정도는 아니고 열심히 하면 할 수 있을 것 같은 느낌이 듭니다.

 

또한 아직 저는 부족한게 많지만, 그래도 믿고 맡겨주신 일이 있으니 잘 완수해보도록 하겠습니다.

 

[스터디 일정]

 

월 : C# 기초 문법 (비주얼스튜디오 설치, 변수, 자료형, 문자열, 입출력, 배열, 함수, 조건문 (if, switch), 반복문 (while, for), Exception, Class & Object, Constructor, Getter & Setter, Static Class, 상속)

 

화 : 로그인, 회원가입, 숫자야구 + PostgreSQL 곁들여서 구현

 

수 : 윈폼 활용하여 로그인, 회원가입, 숫자야구 구현

 

목 아침까지 팀원 서로에게 제출할 문제 인당 3문제씩 제출

 

목~토 : -> 총 15문제 됨 // 하루에 3문제씩 선택해서 풀기

 


 

 

C# 은 .Net Framework 기반에서 구동되는 현대적 객체지향, type-safe 프로그래밍 언어

C, C++ 을 기반으로하는 C family 언어

OOP 이면서 Component-oriented programming language 라는 것

Garbage collection을 지원, Nullable types guard 지원, Exception handling 지원, Lambda Expression 지원

LINQ(Language Integrated Query) 문법 지원, asynchronous operations 지원, object oriented 된 unified type system 지원

유저 정의 reference types 와 value types 지원, 인라인 동적 할당, 다양한 내장 함수, version관리되는 프로그램들과 라이브라리

virtual과 ovveride 연산자, overload, interface 방식 지원되는 특징을 갖는다.

 

.Net architecture

virtual execution system(가상환경 구동)으로써 이를 기반으로 C#이 구동된다. (JAVA의 JVM과 비슷)

이는 common language runtime (이하 CLR) 이라고 부르며, 여러가지 라이브러리를 제공한다.

마이크로소프트가 제작했으며, 이는 국제 표준으로 작동하고 있다.

CLR 상에서 구동되는 프로그램들은 언어가 다르더라도 공통 라이브러리를 기반하고 있기 때문에 서로 매끄럽게 구동될 수 있는 장점이 있다.

 

C#으로 작성된 언어들은 IL(Intermediate language)를 통해 컴파일 되고 있기 때문에, bitmap이나 string들이 assembly로 저장되어 정보를 제공한다. (.dll 확장자 류)

C# 프로그래밍이 실행되면, CLR로 적재되어 Just In Time(JIT) 즉시 IL code 를 통해 번역되고, 자동적으로 가비지 컬랙터, 예외 처리, 리소스 관리가 수행되어 프로그래머의 수고를 덜을 수 있다.

 

닷넷 프레임 워크 기반 언어들(F#, Visual Basic, C++) 끼리 서로 상호작용이 가능하고 현재 20가지 이상의 Common Type Specification(CTS) 언어들이 제공되고 있다.

 

런타임 동안 제공되는 서비스(가비지 컬랙터 같은 것들)을 더불어 닷넷은 광범위한 라이브러리르 제공하고, namespace로 작성된 다양한 기능들을 지원하고 있다. 유용한 기능들이 많고 무료이며, 보안이면 보안, 성능이면 성능 다양한 잠재력을 가지고 있다.

 

 

 

 

.Net architecture 와 JVM 의 차이 (OpenAI기반 셀프 번역, 틀릴 수 있음)

1. 플랫폼과 언어

-> 닷넷은 마이크로소프트 윈도우 기반이지만, .Net Core 가 발표된 이후, macOS 와 Linux 도 지원하고 있음, C# 외에 Visual Basic, F# 같은 언어들 지원함.

-> JVM 이 지원하는 운영체제면 모두 사용가능하도록 설계됨, Oracle 회사(Sun Microsystem)에서 개발되었고, Kotlin, Scala, Groovy 언어를 지원함.

 

2. 언어와 상호작용

-> 닷넷은 CLR(Common Language Runtime) 이라는 시스템을 이용하여, object를 생성하면 다른 언어에서도 이를 활용할 수 있음

-> JVM은 JVM에서 돌아가는 언어면, Java bytecode를 통해서 상호 작용 가능함

(-> 방식은 다르지만, 가상환경 구축을 통해 프로그램끼리 객체를 가져다가 쓸 수 있다는 의미인 것 같음)

 

3. 기초 라이브러리

-> 닷넷은 BCL(Base Class Library)를 제공하여, 미리 제작된 클래스와 프로그래밍 업무들을 제공함

-> 자바는 JSL(Java Standard Library)를 제공하여 마찬가지로 미리 설계된 클래스와 업무들을 제공함

 

4. 모바일 개발

-> 닷넷은 Xamarin을 사용하여 개발자들로 하여금, Android, iOS 를 크로스 플랫폼 빌드를 가능하게 함

-> JVM은 안드로이드 특화되어 Java bytecode로 변환하여 Android device에 적용할 수 있게함

 

5. 프로그래밍 생테계

-> 닷넷은 마이크로소프트 주도의 닷넷 생태계가 강력하고 빠르게 성장했으며, 이 또한 마이크로소프트가 지원해주고 있음

-> JVM은 워낙 사용해온지가 오래되었기 때문에 방대하고 성숙한 생태계가 구성되어있음. Third party library가 이미 즐비하다.

 

6. 오픈소스

-> 닷넷은 .NET Core 의 발표 이후 오픈소스 개발을 내세워 gIt hub에서 코드들을 확인할 수 있다.

-> JVM은 오픈소스들은 아니지만, 이미 많은 JVM based 언어와 라이브러리들이 오픈 소스로 존재하고 있다.

 

둘은 비슷하면서도 차이가 존재하기 때문에, 어느 한쪽이 옳다가 아니라, 상황과 전략에 따라 선택이 필요할 것으로 보인다.

 


"Hello, World"

 

using System;

class Hello
{
    static void Main()
    {
        // This line prints "Hello, World" 
        Console.WriteLine("Hello, World");
    }
}
Hello, World

 

using이라는 예약어를 통해 System이라는 namespace를 참조한다. 그리하여 System.Console.WriteLine("something...");

를 위처럼 줄여쓸 수 있다.

 

static이라고 선언한 Main Method는 "this"라는 예약어 없이도 사용가능하게 하여, C# 프로그램을 구동시키는 Entry Point가 된다.

 

// 를 통해 주석을 달 수 있으며, /* */ 를 통해 문단도 주석 가능

 

System namespace의 Console 클래스의 WriteLine 메서드를 통해 우리는 콘솔에 "Hello, World"를 출력할 수 있었다.

 


자료형과 변수

 

C#에는 값을 저장하는 value type 과 instance를 가리키는 reference type 두가지가 존재함 (파이썬, mutable vs immutable과 비슷)

identifier(구별자)라고 변수의 이름을 지칭하며, 빈칸이 없는 유니코드 글자들로 지정할 수 있으며 @를 붙이면 다른 언어들과 상호작용 가능한 변수로 사용할 수 있게 된다.

 

value 타입들은 더 세분화하여 simple / enum / struct / nullable / tuple 로 구분가능하며, reference 타입들은 class type, interface type, array type, delgate type으로 구분할 수 있다.

 


코드들

 

namespace Gwang
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            Console.WriteLine("    /|");
            Console.WriteLine("   / |");
            Console.WriteLine("  /  |");
            Console.WriteLine(" /   |");
            Console.WriteLine("/____|");
            Console.ReadLine();
        }
    }
}
namespace Gwang
{
    class Program
    {
        static void Main(string[] args)
        {
            string characterName = "John";
            int characterAge;
            characterAge = 35;

            Console.WriteLine("There once was a man named" + characterAge);
            Console.WriteLine("He was " + characterAge + " years old");
            Console.WriteLine("He really liked the name " + characterName);
            Console.WriteLine(string.Format("But didn't like being {0}.", characterAge));

            Console.ReadLine();
        }
    }
}
namespace Gwang
{
    class Program
    {
        static void Main(string[] args)
        {
            string phrase = "Park's Solution";
            char grade = 'e';
            int age = 30;
            int minus_num = -220;
            double gpa = 3.3;
            bool isMale = false;
            bool hasAlive = true;

            Console.WriteLine("Hello!\n");


            Console.ReadLine();
        }
    }
}

 

자료형, 형변환

 


namespace Gwang
{
    class Program
    {
        static void Main(string[] args)
        {
            string phrase = "Giffraffe\n\"Academ'y'\"" + " is cool.";
            // Console.WriteLine(phrase);
            Console.WriteLine(phrase.Length);
            Console.WriteLine(phrase.ToUpper());
            Console.WriteLine(phrase.Contains("cool"));
            Console.WriteLine(phrase[0]);

            Console.WriteLine();
            string phrase_1 = Console.ReadLine();
            Console.WriteLine(phrase_1);

        }
    }
}
namespace Gwang
{
    class Program
    {
        static void Main(string[] args)
        {
            int num = 8;
            Console.WriteLine(num);
            Console.WriteLine(Math.Abs(-111));
            Console.WriteLine(Math.Max(3,2));
            Console.WriteLine(Math.Min(128, 3.5));
            Console.WriteLine(Math.Sqrt(64));
            Console.WriteLine(4 + 50 / 2);
        }
    }
}
namespace Gwang
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("Enter your name: ");
            string name = Console.ReadLine();
            Console.Write("Enter your age: ");
            string age = Console.ReadLine();
            Console.WriteLine("Hello " + name + ", you are " + age + ".");

            Console.ReadLine();
        }
    }
}
namespace Gwang
{
    class Program
    {
        static void Main(string[] args)
        {
            int num = Convert.ToInt32("45");
            Console.WriteLine(num + 6);

            Console.Write("Enter a number: ");
            double num1 = Convert.ToDouble(Console.ReadLine());
            Console.Write("Enter a another number: ");
            double num2 = Convert.ToDouble(Console.ReadLine());
            Console.WriteLine(num1 + num2);
        }
    }
}
namespace Gwang
{
    class Program
    {
        static void Main(string[] args)
        {
            string color, pluralNoun, celebrity;

            Console.Write("Enter a color: ");
            color = Console.ReadLine();

            Console.Write("Enter a plural noun: ");
            pluralNoun = Console.ReadLine();

            Console.Write("Enter celebrity: ");
            celebrity = Console.ReadLine();

            // Madlib
            Console.WriteLine("Roses are {0}.", color);
            Console.WriteLine("{0} are blue.", pluralNoun);
            Console.WriteLine("I love {0}. ",celebrity);
        }
    }
}

 

 

감사합니다.

 

 

# test_server_launcher.py
from Back.db_connector import DBConnector
from Back.server import EMRServer

if __name__ == '__main__':
    db_conn = DBConnector(test_option=True)
    db_conn.create_tables()
    from data.insert_data_to_tables import *

    insert_KTAS_data(db_conn)
    insert_dummy_employee_data(db_conn, 30)
    insert_dummy_chat_room(db_conn, 30)
    insert_dummy_employee_chat_room_data(db_conn, 20, (1, 30), (1, 30))
    server = EMRServer(db_conn)
    server.start()

 

프로젝트명 미니 의료 정보 시스템 만들기
개발 인원 박광현 (1명)
개발 기간 2023년 07월 17일 ~ 23일 19시 (6일간)
개발 장소 광주인력개발원 생산정보 시스템실
과제 내용 이전 프로젝트에서 사용했던 파이썬과 네트워크, SQL을 다시 공부하고 모든 내용을 포함시켜

파이썬과 네트워크, SQL 전체를 아우르는 복습이 가능한 프로그램을 만들어오기

개발 결과 1. 환자 접수 기능 - 미구현

2. 로그인 기능 구현

3. 실시간 입실 환자 연동 구현

4. 실시간 직원 목록 및 부서 리스트 제공 구현

5. KTAS 리스트 자료 저장 구현

6. DashBoard 미구현


기능 설명 [구조 개요]

(서버파트)

파이썬 socket 모듈을 사용하여 소켓 통신(TCP/IP)을 사용한다. 현재는 Loopback IP, Port 9999를 사용하고 있으나, Common 패키지 내의 클래스 변수를 변경하면 이를 참조하여 변경할 수 있다.


파이썬 내장 Select 모듈을 사용하여 socket list 를 관리하고 listening하고 있으며, 블로킹 구조를 사용 중, timeout을 0.1초로 설정하였다. 구동된 서버는 쓰레드로 "receive_message" 라는 함수를 통해 들어온 데이터를 헤더에 따라 처리한다.


헤더와 데이터는 ASCII code 2번(start of text)를 통해 구분하고 있으며, 들어온 데이터 스트림에 대해 split을 시행시켜, 명령과 데이터를 구분하여 전달 받았다.


들어온 데이터는 이어서, 직접 커스텀한 ObjEncoder / ObjDecoder 클래스를 사용하였고, 이를 통해 원하는 형태로 자유자재로 데이터를 보내고 받는게 가능해졌다. 해당 클래스는 Common에 있어, Server class 와 Client class 가 같이 사용하고 있고, 패키지를 따로 두었기 때문에, 따로 모듈화 가능한 장점이 있다. 또한, eval 함수를 사용하지 않았고 오류가 발생하면 해당 요청을 무시해버리도록 설계하여 보안과 내구성도 이전 프로젝트에 비해서 올라간 것이 특징이다.


서버를 구동시키면 Dependency Injection 된 DBConnector 인스턴스를 이용 DB 접근 가능하고, 객체 또는 해당 테이블 ID 를 통해서 객체를 INSERT하거나 SELECT 할 수 있다. ORM을 계획하고 작성하였기 때문에, 설계하는데 시간은 조금 걸렸지만, 관계와 속성이 확정되고 함수가 만들어지기 시작하니까 기능 추가하는 것이 아주 수월해졌다.


(DB 파트)

네트워크 연습을 위해 주로 사용했던 된 테이블은 Employee 와 ChatRoom이다. 이 둘은 서로 다대 다 관계이기 때문에 Employee_Chatroom 테이블로 일대 다, 다대 일 관계로 풀어내었고, SQL 로직과 파이썬 로직을 합해 이전과 대화를 했던 적이 있던 상대라면 이전의 대화방을 불러주고, 그게 아니라면 새로운 Chat room row 를 만들어 autoincrement되는 ID를 서버가 받고, 이를 다시 클라이언트가 받아 해당 방에 message sending을 할 수 있게 설계하였다.


수월한 테스트를 위해 따로 SQL 파일을 작성하여 DBConnector에서 executeScript를 실행하게끔 하였고, 더미 데이터를 입력하여 테스트 시간을 단축시키고 효과적으로 상황에 대응하기 위해 노력하였다. 다만 이 방법은 몇번째 줄에서 오류가 났는지 알려주지 않기 때문에 어느정도 숙련된 SQL 작성자에게 권한다. (그래도 잘 모르겠으면 직접 script 를 옮겨다가 browser에서 실행시키거나 cli 에서 작성하면 몇번째 줄인지는 알려준다.)


(클라이언트 파트)

SOLID SRP를 준수하기 위해 Connector 클래스와 Controller 클래스를 구분하였다.

Connector는 서버소켓과 연결, Controller 는 인스턴스 변수로 각 위젯들을 생성하고, 관리하고 있으며, PyQtSignal을 통해 Connector로 들어온 메시지에 대해 Controller를 핸들링하거나, 메모리에 필요한 정보를 미리 가지고 있다가 Controller에서 요청이 있을 때 참조한다.


Controller 에서는 조작 부분(창 띄우기, 닫기 또는 connector로의 함수 호출, connector로부터 emitted 된 신호들을 처리) 기능을 맡고 있고 Connector는 controller로부터 들어온 함수를 실질적으로 server로 보내거나, server로 부터 들어온 데이터 처리 및 저장, signal 호출 등의 역할을 구분하였다.


[클래스 설명]

DBConnector: server 에 sqlite 연결 기능 제공, 여러 connection pool을 사용하지 않도록 singleton 패턴으로 설계함

EMRServer: 서버 기능 클래스

Connector: 클라이언트(통신) 클래스

WidgetController: 클라이언트(컨트롤러) 클래스

Common: 공통적으로 사용하는 클래스(상수나 연속적으로 사용하는 str 을 저장하여 오타로 인한 오류를 억제하고자 사용하였음

ObjEncoder/ObjDecoder 클래스: object 또는 list(tuple), list(dict) 구조를 json 또는 formatted string으로 만든뒤 UTF-8 로 인코딩/디코딩하는 역할을 함

FakeDataMaker: 파이썬 Faker 모듈을 사용해 테스트용 더미 데이터를 생성하는데 사용함


[DAO]

BedOfWard

ChatRoom

Department

EmergencyNurseRecord

EmployeeChatRoom

KTAS

Message

일정표 표1) 참조
요구분석표 표2) 참조
ERD 그림 1) 참조
구동 영상 및 이미지 하단 첨부 이미지 및 영상 참조 (첨부파일 동봉)
소스 코드 하단 소스 코드 또는 첨부파일 참조
개발 후기
정말 순식간에 마감일이 되어버렸다. 완성도 있는 결과물보다는 판을 너무 크게 벌려놓은 탓에 수습을 못한 부분이 많은데, 매 순간 막히는 부분이 생겨도 어떻게든 문제점을 들춰내 하나씩 해결해나가다 보면 프로그램이 예상대로 구동되고, 하나씩 동작이 되면서 성취감을 느꼈다. 미완성되어 미구현한 부분들도 시간이 있었다면 다 만들어낼 수 있었을 것이다. 다양한 데이터 전송과 많은 타이핑을 경험했고, 아직 두렵긴 하지만 네트워크가 가능하면 할 수 있는 일이 무진장 많아지기 때문에 있는 시간을 최선을 다하고 최대한 활용했다.


또한, 많은 동료 수강생들이 비슷한 문제를 맞닥뜨리고 있을 때, 같이 고민해주려고 노력하였고, 엄연히 개인 프로젝트지만 스터디를 같이하는 팀플레이 요소도 있었기 때문에, 중간에 그룹스터디도 진행하고 팀원들의 진행사항을 계속 속속이 파악했다. 부득이하게 멀리 튕겨나간 사람도 있었다. 다시 한번 느끼는 일이지만, 살아남은 자가 강하고, 강한자가 살아 남는다. 가만히 있지도 말 것이고, 이렇게 버티고 있는 것도 잘하는 것이라 스스로 위로해본다.


이번 프로젝트에서 가장 아쉬운 점은 통일성이 부족한 DTO 객체들과 복잡한 DAO 관계 및 상태로 인해 빠른 시간안에 기대했던 모든 기능을 구현하지 못했다는 것이다. 만일 이 경험 상태로 다시 프로젝트 처음으로 돌아간다면, UI 제작 -> 테이블 제작 -> 송수신 데이터 설계 -> 테이블 수정 보완 -> 구현 순으로 진행할 것 같다. 이번 프로젝트는 UI 제작 -> 테이블 제작 -> UI 기능 구현 + 송수신 데이터 동시에 진행하다 보니, 매끄럽게 진행되지 못했다.


미숙한 부분이 많았지만, 이전 프로젝트에서 거의 실패라고 느껴졌던 실시간 네트워크 통신에 대해 많은 부분 보완할 수 있었고, 거진 한달 전에 진행했던 QtFlowSheet 개인 프로젝트에 비해서도 많은 성장이 이뤄졌다.


테이블은 총 14개가 나왔다. 처음엔 12개 정도 테이블이 나왔는데, 중간에 관계 요소가 누락되었거나, 데이터 저장을 위해 추가해야하는 테이블이 더 늘어나면서 부득이하게 수정이 필요했다. 이에 대해 대응하는데도 좋은 경험이 되었다. 이전에 비해 확실히 딜레이 없이 SQL이 자동으로 작성되고 있고, 테이블 관계가 작성된다. 프로젝트 첫날에 RDBMS 의 관계 구조에 대해 동료 수강생들과 스터디를 진행한 것이 큰 도움이 되었다. 하지만 아직 부족한 것이 많기 때문에 계속 연습해볼 예정이다.

 

 

표1) 일정표

 

개발 기간

표2) 요구분석표

 

그림 1) ERD (Entity Relationship Diagram)

 

 

 

 

 

 

 

 

 

 


소스코드

 

# db_connector.py
import datetime
import sqlite3

import numpy as np
import pandas as pd

from Domain.chat_room import ChatRoom
from Domain.emergency_nurse_record import EmergencyNurseRecord
from Domain.message import Message
from Domain.people._employee import Employee
from Domain.people.patient import Patient


class DBConnector:
    # ===================== BASIC ============================ #
    _instance = None
    SQL_PATH = r"../Back/table_creation.sql"

    def __new__(cls, test_option=False):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self, test_option=False):
        self.conn = None
        self.test_option = test_option

    def start_conn(self):
        if self.test_option is False:
            self.conn = sqlite3.connect('../test/main_storage.db')
        else:
            self.conn = sqlite3.connect('../test/test_storage.db')
        return self.conn.cursor()

    def end_conn(self):
        if self.conn is not None:
            self.conn.close()
            self.conn = None

    def commit_db(self):
        self.conn.commit()

    @staticmethod
    def set_pandas_configure():
        pd.set_option('display.max_columns', None)
        pd.set_option('display.max_rows', None)
        pd.set_option('display.width', 1000)

    @staticmethod
    def make_prepared_statement(object_, table_name):
        if hasattr(object_, 'to_dict_for_insert'):
            key_words = object_.to_dict_for_insert().keys()
            keys = ','.join(key_words)
            values = object_.to_dict_for_insert()
        else:
            key_words = object_.__dict__.keys()
            keys = ','.join(object_.__dict__.keys())
            values = object_.__dict__

        colon_keys = ','.join([f':{x}' for x in key_words])
        # print('#key_words#',key_words)
        # print('#keys#',keys)
        # print('#values#',values)
        # print('#colon_keys#',colon_keys)
        pstmt = f'insert into {table_name} ({keys}) values ({colon_keys})'

        return pstmt, values

    def find_obj_by_id_and_table_name(self, obj_id, table_name, class_name):
        assert isinstance(obj_id, int) and isinstance(table_name, str)
        c = self.start_conn()
        fetched_row = c.execute(f'select * from {table_name} where {class_name}_id = {obj_id}').fetchone()
        self.end_conn()
        return fetched_row

    def find_all_by_table_name(self, table_name):
        assert isinstance(table_name, str)
        c = self.start_conn()
        fetched_row_list = c.execute(f'select * from {table_name}').fetchall()
        self.end_conn()
        return fetched_row_list

    # ================ CREATE TABLE ============================ #
    def create_tables(self):
        c = self.start_conn()
        pstmt = None
        with open(self.SQL_PATH, 'r', encoding='utf-8') as file:
            pstmt = file.read()
        if pstmt is None:
            raise 'sql문 못불러옴'
        c.executescript(pstmt)
        self.commit_db()
        self.end_conn()
        print('created_tables')

    # ===================== DOMAIN ============================ #
    # ================ EMPLOYEE ============================ #
    def insert_employee(self, employee: Employee):
        assert isinstance(employee, Employee)
        c = self.start_conn()
        if employee.employee_id is None:
            # insert
            pstmt, values, = self.make_prepared_statement(employee, 'tb_employee')
            c.execute(pstmt, values)
        else:
            # todo: update 로직 필요하면 작성
            return
        self.commit_db()
        row = c.execute('select * from tb_employee order by employee_id desc limit 1').fetchone()
        result = Employee(*row)
        self.end_conn()
        return result

    def find_employee_by_id(self, employee_id):
        fetched_row = self.find_obj_by_id_and_table_name(employee_id, "tb_employee", "employee")
        return Employee(*fetched_row)

    def find_all_employee(self):
        fetched_rows = self.find_all_by_table_name("tb_employee")
        result_list = list()
        for row in fetched_rows:
            result_list.append(Employee(*row))
        return result_list
            def find_all_employee_department(self):
        all_employee_list = self.find_all_employee()
        c = self.start_conn()
        result_list = list()
        for e in all_employee_list:
            name = None
            if e.type_job == 1:
                name = c.execute(f"""
                    select name from tb_department where department_id = 
                        (select assigned_department_id from tb_doctor where employee_id = {e.employee_id})
                """).fetchone()
            elif e.type_job ==2:
                name = c.execute(f"""
                    select name from tb_department where department_id = 
                        (select assigned_ward_id from tb_nurse where employee_id = {e.employee_id})
                """).fetchone()
            elif e.type_job ==3:
                name = c.execute(f"""
                    select name from tb_department where department_id = 
                        (select assigned_part_id from tb_administration where employee_id = {e.employee_id})
                """).fetchone()

            if name is None:
                name = "-"
            else:
                name = name[0]
            result_list.append(name)
        return result_list

    def assert_login_username_and_password(self, username, password):
        c = self.start_conn()
        fetched_row = c.execute(f'select * from tb_employee where login_username=? and login_password=?',
                                (f'{username}', f'{password}',)).fetchone()

        if fetched_row is None:
            return None
        return Employee(*fetched_row)
    # ================ DOCTOR / NURSE / ADMIN ============================ #
    def insert_doctor_job(self, employee, assigned_department_id):
        c = self.start_conn()
        c.execute(f'''insert into tb_doctor(employee_id, assigned_department_id)
            values (?, ?)''', (employee.employee_id, assigned_department_id,))
        self.commit_db()
        self.end_conn()

    def insert_nurse_job(self, employee, assigned_ward_id):
        c = self.start_conn()
        c.execute(f'''insert into tb_nurse(employee_id, assigned_ward_id)
                    values (?, ?)''', (employee.employee_id, assigned_ward_id,))
        self.commit_db()
        self.end_conn()

    def insert_admin_job(self, employee, assigned_part_id):
        c = self.start_conn()
        c.execute(f'''insert into tb_administration(employee_id, assigned_part_id)
                    values (?, ?)''', (employee.employee_id, assigned_part_id,))
        self.commit_db()
        self.end_conn()

    def find_doctor_name_by_doctor_id(self, doctor_id):
        c = self.start_conn()
        fetched_row = c.execute(f"""
            select name from tb_employee where employee_id =
                (select employee_id from tb_doctor where doctor_id = ?)
        """, (doctor_id, )).fetchone()
        if fetched_row is not None:
            result = fetched_row[0]
        else:
            result = ''
        self.end_conn()
        return result

    def find_nurse_name_by_nurse_id(self, nurse_id):
        c = self.start_conn()
        fetched_row = c.execute(f"""
            select name from tb_employee where employee_id =
                (select employee_id from tb_nurse where nurse_id = ?)
        """, (nurse_id, )).fetchone()
        if fetched_row is not None:
            result = fetched_row[0]
        else:
            result = ''
        self.end_conn()
        return result

    # ================ CHAT ROOM ============================ #
    def insert_chat_room(self, chat_room: ChatRoom):
        assert isinstance(chat_room, ChatRoom)
        c = self.start_conn()
        if chat_room.chat_room_id is None:
            # insert
            pstmt, values, = self.make_prepared_statement(chat_room, 'tb_chat_room')
            c.execute(pstmt, values)
        else:
            # todo: update 로직 필요하면 작성
            pass
        self.commit_db()
        row = c.execute('select * from tb_chat_room order by chat_room_id desc limit 1').fetchone()
        result = ChatRoom(*row)
        self.end_conn()
        return result

    def create_chat_room(self, login_employee_id, request_employee_id):
        c = self.start_conn()
        now_time_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        c.execute(f"""
            insert into tb_chat_room(created_time) values (?)
        """, (f"{now_time_str}",))
        self.commit_db()
        last_index = c.execute(f"""
            select chat_room_id from tb_chat_room order by chat_room_id desc limit 1
        """).fetchone()
        if last_index is None:
            raise 'insert 실패'
        last_index = last_index[0]

        c.executescript(f"""
            insert into tb_employee_chat_room(chat_room_id,employee_id) 
                values ({last_index}, {login_employee_id}),
                        ({last_index}, {request_employee_id});
        """)
        self.commit_db()
        self.end_conn()
    
    def find_chat_room_by_two_employee_id(self, login_employee_id, request_employee_id):
        assert isinstance(login_employee_id, int) and isinstance(request_employee_id, int)
        c = self.start_conn()
        login_employee_chat_room_list = set()
        request_employee_chat_room_list = set()

        fetched_row = c.execute(f"""
            select chat_room_id from tb_employee_chat_room where employee_id = {login_employee_id}   
        """).fetchall()
        for i in fetched_row:
            login_employee_chat_room_list.update(i)

        fetched_row = c.execute(f"""
                    select chat_room_id from tb_employee_chat_room where employee_id = {request_employee_id}   
                """).fetchall()
        for i in fetched_row:
            request_employee_chat_room_list.update(i)

        intersection_chat_room_set = login_employee_chat_room_list.intersection(request_employee_chat_room_list)
        if len(intersection_chat_room_set) == 0:
            intersection_chat_room_id = self.create_chat_room(login_employee_id, request_employee_id)
        else:
            intersection_chat_room_id= intersection_chat_room_set.pop()
        print("intersection_chat_room_id: ", intersection_chat_room_id)
        return intersection_chat_room_id


    def find_chat_room_by_id(self, chat_room_id):
        assert isinstance(chat_room_id, int)
        fetched_row = self.find_obj_by_id_and_table_name(chat_room_id, "tb_chat_room", "chat_room")
        # 관련 톡방 사용자(2명) list 가져오기
        employee_list = self.find_employees_by_chat_room_id(chat_room_id)
        # message_list 가져오기
        message_list = self.find_messages_by_chat_room_id(chat_room_id)

        return ChatRoom(*fetched_row, employee_list=employee_list, message_list=message_list)

    def find_employees_by_chat_room_id(self, chat_room_id):
        c = self.start_conn()
        fetched_rows = c.execute(f"""
            select * from tb_employee where employee_id in 
            (select employee_id from tb_employee_chat_room where chat_room_id = {chat_room_id})
        """).fetchall()
        result_list = list()
        for row in fetched_rows:
            result_list.append(Employee(*row))
        self.end_conn()
        return result_list

    # ================ EMPLOYEE CHAT ROOM ============================ #
    def insert_employee_chat_room(self, employee_id, chat_room_id):
        assert isinstance(employee_id, int) and isinstance(chat_room_id, int)
        c = self.start_conn()
        c.execute('insert into tb_employee_chat_room(employee_id, chat_room_id) values (?, ?)',
                  (employee_id, chat_room_id))
        self.commit_db()
        self.end_conn()
    
    
    # ================ EmergencyNurseRecord ============================ #
    def find_emergency_nurse_record_by_register_id(self, register_id):
        c = self.start_conn()
        fetched_row = c.execute("""
            select * from tb_emergency_nurse_record where register_id = ?
        """, (register_id,)).fetchone()
        if fetched_row is not None:
            return EmergencyNurseRecord(*fetched_row)
        return None
    # ================ patient ============================ #
    def find_all_patient(self):
        fetched_rows = self.find_all_by_table_name("tb_patient")
        result_list = list()
        for row in fetched_rows:
            result_list.append(Patient(*row))
        return result_list

    def find_patient_by_bed_id(self, bed_id):
        c = self.start_conn()
        fetched_row = c.execute("""
            select * from tb_patient where using_bed_id = ?
        """, (bed_id, )).fetchone()
        if fetched_row is not None:
            return Patient(*fetched_row)
        return None


    def find_bed_id_and_name_list_by_employee_id(self, employee_id):
        c = self.start_conn()

        job_code = self.find_employee_by_id(employee_id).type_job
        table_name = ''
        if job_code == 1:
            table_name = 'tb_doctor'
            col_name  = 'assigned_department_id'
        elif  job_code == 2:
            table_name = 'tb_nurse'
            col_name = 'assigned_ward_id'
        else:
            table_name = 'tb_admin'
            col_name = 'assigned_part_id'

        fetched_rows = c.execute(f"""
            select bed_id, viewing_bed_name from tb_bed_of_ward 
                where assigned_ward_id = 
                (select {col_name} from {table_name} where employee_id =?)
        """, (employee_id,)).fetchall()

        bed_id_list = list()
        name_list = list()

        for row in fetched_rows:
            bed_id_list.append(row[0])
            name_list.append(row[1])

        self.end_conn()
        return bed_id_list, name_list

    # ================ MESSAGE ============================ #
    def insert_message(self, message: Message):
        assert isinstance(message, Message)
        c = self.start_conn()
        if message.message_id is None:
            # insert
            pstmt, values, = self.make_prepared_statement(message, 'tb_message')
            c.execute(pstmt, values)
        else:
            # todo: update 로직 필요하면 작성
            pass
        self.commit_db()
        row = c.execute('select * from tb_message order by message_id desc limit 1').fetchone()
        result = Message(*row)
        self.end_conn()
        return result

 
    def find_messages_by_message_id(self, message_id):
        c = self.start_conn()
        fetched_row = c.execute(f"""
            select * from tb_message where message_id = {message_id})
        """).fetchone()
        result = Message(*fetched_row)
        self.end_conn()
        return result

    def find_messages_by_chat_room_id(self, chat_room_id):
        c = self.start_conn()
        fetched_rows = c.execute(f"""
            select * from tb_message where chat_room_id = ?
        """, (chat_room_id, )).fetchall()
        result_list = list()
        for row in fetched_rows:
            result_list.append(Message(*row))
        self.end_conn()
        return result_list

    def checked_message_by_chat_room_id(self, chat_room_id):
        c = self.start_conn()
        fetched_rows = c.execute(f"""
            update tb_message set "is_confirmed"=1 where message_id in 
            (select employee_id from tb_employee_chat_room where chat_room_id = {chat_room_id})
        """).fetchall()
        result_list = list()
        for row in fetched_rows:
            result_list.append(Message(*row))
        self.end_conn()
        return result_list
# server.py
import time
import traceback
import socket
from threading import Thread

import select

from Back.db_connector import DBConnector
from Common.class_common import Common
from Common.class_json_converter import ObjDecoder, ObjEncoder
from Domain.dto.dto_class import DTOMaker
from Domain.message import Message
from Domain.people._employee import Employee


class EMRServer:
    def __init__(self, db_connector: DBConnector):
        self.db_conn = db_connector
        self.common = Common()

        self.server_socket = None
        self.sockets_list = list()
        self.clients = dict()
        self.thread_for_run = None
        self.run_signal = True

        self.decoder = ObjDecoder()
        self.encoder = ObjEncoder()

        self.dto_maker = DTOMaker()

    def start(self):
        if self.thread_for_run is not None:  # 실행중이면 종료 시키기
            return
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # AF_INET(ipv4를 의미)
        self.server_socket.bind((self.common.HOST, self.common.PORT))  # 바인딩
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_socket.listen()  # 리슨 시작
        self.sockets_list.clear()  # 소켓리스트 클리어
        self.sockets_list.append(self.server_socket)
        self.run_signal = True
        self.thread_for_run = Thread(target=self.run)
        self.thread_for_run.start()
        print('SERVER STARTED')

    def run(self):
        while True:
            if self.run_signal is False:
                break
            try:
                read_sockets, _, exception_sockets = select.select(self.sockets_list, [], self.sockets_list, 0.1)
            except Exception:
                continue
            for notified_socket in read_sockets:
                if notified_socket == self.server_socket:
                    client_socket, client_address = self.server_socket.accept()
                    print(client_address, '연결됨')
                    user = self.receive_message(client_socket)
                    if user is False:
                        continue
                    self.sockets_list.append(client_socket)
                    self.clients[client_socket] = user

                else:
                    message = self.receive_message(notified_socket)
                    if message is False:
                        self.sockets_list.remove(notified_socket)
                        print(notified_socket, '연결해제됨')
                        del self.clients[notified_socket]

            for notified_socket in exception_sockets:
                self.sockets_list.remove(notified_socket)
                print(notified_socket, '오류로 연결해제됨')
                del self.clients[notified_socket]

    def send_message(self, client_socket: socket, header, data):
        assert isinstance(header, str) or isinstance(header, bytes)
        assert isinstance(data, str) or isinstance(data, bytes)
        if isinstance(header, bytes):
            header_to_sending = header.decode(self.common.FORMAT)
        else:
            header_to_sending = header
        if isinstance(data, bytes):
            data_to_sending = data.decode(self.common.FORMAT)
        else:
            data_to_sending = data

        message_to_send = f"{self.common.START_OF_TEXT}".join([header_to_sending, data_to_sending])
        if len(message_to_send) >= self.common.BUFFER:
            raise "부족한 버퍼 용량"
        message_to_send = f"{message_to_send:<{self.common.BUFFER}}"

        print(f"SERVER SENDED: (HEADER: {header} | DATA: {data})")
        client_socket.send(message_to_send.encode(self.common.FORMAT))
        
    def header_data_distributor(self, recv_encoded_message):
        decoded_message = recv_encoded_message.decode(self.common.FORMAT).strip()
        header, data_decoded_str, = decoded_message.split(self.common.START_OF_TEXT)
        data = self.decoder.binary_to_obj(data_decoded_str)
        return header, data

    def receive_message(self, client_socket: socket):
        try:
            recv_encoded_message = client_socket.recv(self.common.BUFFER)
            if len(recv_encoded_message) < 3:
                return True
            request_header, request_data = self.header_data_distributor(recv_encoded_message)
            print(f"SERVER RECEIVED: ({request_header},{request_data})")

        except Exception:
            traceback.print_exc()
            return False

        if request_header == self.common.LOGIN_ACCESS_REQ:
            username = request_data['login_username']
            password = request_data['password']
            login_employee = self.db_conn.assert_login_username_and_password(username, password)
            if login_employee is None:
                self.send_message(client_socket, self.common.LOGIN_ACCESS_RES, self.common.FALSE)
            else:
                # 로그인 성공 로직
                # 회원 정보 보내기
                data_str = self.encoder.toJSON_an_object(login_employee)
                self.send_message(client_socket, self.common.LOGIN_ACCESS_RES, data_str)
                # patient_list 보내기
                # sample_patient_list = f"""{[("IC-1", "김철수(M/55)", "김순재/조운", "접수중"),
                #                             ("IC-2", "관우(M/22)", "김순재/박사라", "접수중")]}"""
                stored_patient_list = list()
                patient_table_list = list()
                bed_id_list, name_list, = self.db_conn.find_bed_id_and_name_list_by_employee_id(
                    login_employee.employee_id)
                patient_list = list()
                doctor_list = list()
                state_list = list()

                first_patient_index = -1
                for idx, i in enumerate(bed_id_list):
                    temp = self.db_conn.find_patient_by_bed_id(i)
                    stored_patient_list.append(temp)
                    if temp is None:
                        patient_list.append('')
                        doctor_list.append('')
                        state_list.append('')
                    else:
                        if first_patient_index == -1:
                            first_patient_index = idx
                        patient_list.append(temp.get_name_and_age())
                        doctor_name = self.db_conn.find_doctor_name_by_doctor_id(temp.assigned_doctor_id)
                        nurse_name = self.db_conn.find_nurse_name_by_nurse_id(temp.assigned_nurse_id)
                        doctor_list.append(f"{doctor_name}/{nurse_name}")
                        state_list.append('접수중')

                for row in zip(name_list, patient_list, doctor_list, state_list):
                    patient_table_list.append(row)
                self.send_message(client_socket, self.common.PATIENT_NAMELIST_RES, f"{patient_table_list}")
                print('first_patient_index:', first_patient_index)
                # patient 보내기
                if first_patient_index != -1:
                    self.send_message(client_socket, self.common.PATIENT_RES,
                                      f"{stored_patient_list[first_patient_index]}")
                    time.sleep(0.2)
                    # MEDICAL_ORDER_RES
                    emergency_nurse_record = self.db_conn.find_emergency_nurse_record_by_register_id(
                        stored_patient_list[first_patient_index].register_number)
                    self.send_message(client_socket, self.common.EMERGENCY_NURSE_RECORD_RES,
                                      f"{emergency_nurse_record}")
                # all_employee 보내기
                employee_list = self.db_conn.find_all_employee()
                self.send_message(client_socket, self.common.ALL_EMPLOYEE_LIST_RES,
                                  f"{employee_list}")
                # all_employee_department 보내기
                all_employee_department = self.db_conn.find_all_employee_department()
                self.send_message(client_socket, self.common.ALL_EMPLOYEE_DEPARTMENT_LIST_RES,
                                  f"{all_employee_department}")

	
        elif request_header == self.common.CHAT_ROOM_REQ:
            assert isinstance(request_data, tuple)
            login_employee_id, request_employee_id= request_data
            chat_room_id = self.db_conn.find_chat_room_by_two_employee_id(login_employee_id, request_employee_id)
            chat_room = self.db_conn.find_chat_room_by_id(chat_room_id)
            self.send_message(client_socket, self.common.CHAT_ROOM_RES, self.encoder.toJSON_an_object(chat_room))
            message_list = self.db_conn.find_messages_by_chat_room_id(chat_room_id)
            self.send_message(client_socket, self.common.MESSAGE_LIST_RES, self.encoder.toJSON_an_object(message_list))

        elif request_header == self.common.SEND_A_MESSAGE_REQ:
            assert isinstance(request_data, Message)
            arrived_message = request_data
            processed_message = self.db_conn.insert_message(arrived_message)
            data = self.encoder.toJSON_an_object(processed_message)
            for s in self.sockets_list:
                if s != self.server_socket:
                    self.send_message(s, self.common.SEND_A_MESSAGE_RES, data)



        return True
# table_creation.SQL
DROP TABLE IF EXISTS tb_ktas;
CREATE TABLE "tb_ktas" (
	"ktas_id"	INTEGER,
	"first_category_name"	TEXT NOT NULL,
	"first_category_code"	TEXT NOT NULL,
	"second_category_name"	TEXT NOT NULL,
	"second_category_code"	TEXT NOT NULL,
	"third_category_name"	TEXT NOT NULL,
	"third_category_code"	TEXT NOT NULL,
	"fourth_category_name"	TEXT NOT NULL,
	"fourth_category_code"	TEXT NOT NULL,
	"final_grade"	TEXT NOT NULL,
	"ktas_code"	TEXT NOT NULL,
	PRIMARY KEY("ktas_id" AUTOINCREMENT)
);

DROP TABLE IF EXISTS tb_employee;
CREATE TABLE "tb_employee" (
	"employee_id"	INTEGER,
	"name"	TEXT NOT NULL,
	"type_job"	INTEGER NOT NULL,
	"login_username"	TEXT NOT NULL UNIQUE,
	"login_password"	TEXT NOT NULL,
	"mobile_phone_num_1"	TEXT,
	"mobile_phone_num_2"	TEXT,
	PRIMARY KEY("employee_id" AUTOINCREMENT)
);

DROP TABLE IF EXISTS tb_department;
CREATE TABLE "tb_department" (
    "department_id"	INTEGER,
    "name"	TEXT NOT NULL,
    "job_category"	INTEGER NOT NULL,
    PRIMARY KEY("department_id" AUTOINCREMENT)
);
DROP TABLE IF EXISTS tb_doctor;
CREATE TABLE "tb_doctor" (
	"doctor_id"	INTEGER,
	"employee_id"	INTEGER NOT NULL,
	"assigned_department_id"	INTEGER NOT NULL,
	PRIMARY KEY("doctor_id" AUTOINCREMENT)
	FOREIGN KEY("assigned_department_id") REFERENCES "tb_department"("department_id")
);

DROP TABLE IF EXISTS tb_nurse;
CREATE TABLE "tb_nurse" (
	"nurse_id"	INTEGER,
	"employee_id"	INTEGER NOT NULL,
	"assigned_ward_id"	INTEGER NOT NULL,
	PRIMARY KEY("nurse_id" AUTOINCREMENT),
	FOREIGN KEY("employee_id") REFERENCES "tb_employee"("employee_id")
	FOREIGN KEY("assigned_ward_id") REFERENCES "tb_department"("department_id")
);

DROP TABLE IF EXISTS tb_administration;
CREATE TABLE "tb_administration" (
    "administration_id"	INTEGER,
    "employee_id"	INTEGER NOT NULL,
    "assigned_part_id"	INTEGER NOT NULL,
    PRIMARY KEY("administration_id" AUTOINCREMENT),
    FOREIGN KEY("employee_id") REFERENCES "tb_employee"("employee_id"),
    FOREIGN KEY("assigned_part_id") REFERENCES "tb_department"("department_id")
);
DROP TABLE IF EXISTS tb_chat_room;
CREATE TABLE "tb_chat_room" (
    "chat_room_id"	INTEGER,
    "created_time"	TEXT NOT NULL,
    PRIMARY KEY("chat_room_id" AUTOINCREMENT)
);
DROP TABLE IF EXISTS tb_employee_chat_room;
CREATE TABLE "tb_employee_chat_room" (
	"employee_chat_room_id"	INTEGER,
	"chat_room_id"	INTEGER NOT NULL,
	"employee_id"	INTEGER NOT NULL,
	FOREIGN KEY("chat_room_id") REFERENCES "tb_chat_room"("chat_room_id"),
	FOREIGN KEY("employee_id") REFERENCES "tb_employee"("employee_id"),
	PRIMARY KEY("employee_chat_room_id" AUTOINCREMENT)
);

DROP TABLE IF EXISTS tb_patient;
CREATE TABLE "tb_patient" (
	"patient_id"	INTEGER,
	"birth_date"	TEXT,
	"name"	TEXT NOT NULL,
	"sex"	TEXT NOT NULL,
	"ssn"	TEXT UNIQUE,
	"address"	TEXT,
	"type_insurance"	TEXT,
	"using_bed_id"	INTEGER,
	"register_number"	INTEGER UNIQUE,
	"assigned_doctor_id"	INTEGER,
	"assigned_nurse_id"	INTEGER,
	FOREIGN KEY("using_bed_id") REFERENCES "tb_bed_of_ward"("bed_id"),
	FOREIGN KEY("assigned_doctor_id") REFERENCES "tb_doctor"("doctor_id"),
	FOREIGN KEY("assigned_nurse_id") REFERENCES "tb_nurse"("nurse_id"),
	PRIMARY KEY("patient_id" AUTOINCREMENT)
);

DROP TABLE IF EXISTS tb_bed_of_ward;
CREATE TABLE "tb_bed_of_ward" (
    "bed_id"	INTEGER,
    "assigned_ward_id"	INTEGER NOT NULL,
    "additional_info"	TEXT,
    "viewing_bed_name"	TEXT NOT NULL,
    PRIMARY KEY("bed_id" AUTOINCREMENT),
    FOREIGN KEY("assigned_ward_id") REFERENCES "tb_department"
);

DROP TABLE IF EXISTS tb_emergency_nurse_record;
CREATE TABLE "tb_emergency_nurse_record" (
	"enr_id"	INTEGER,
	"register_id"	INTEGER,
	"onset_time"	TEXT,
	"ktas_id"	INTEGER,
	"cheif_complain"	TEXT,
	"description"	TEXT,
	"recorder_nurse_id"	INTEGER,
	"saved_time"	TEXT,
	"responser"	TEXT,
	"memo"	TEXT,
	PRIMARY KEY("enr_id" AUTOINCREMENT),
	FOREIGN KEY("recorder_nurse_id") REFERENCES "tb_nurse"("nurse_id")
	FOREIGN KEY("register_id") REFERENCES "tb_patient"("register_number")
);



DROP TABLE IF EXISTS tb_message;
CREATE TABLE "tb_message" (
	"message_id"	INTEGER,
	"sender_employee_id"	INTEGER NOT NULL,
	"chat_room_id"	INTEGER NOT NULL,
	"contents"	TEXT NOT NULL,
	"is_confirmed"	INTEGER NOT NULL,
	FOREIGN KEY("chat_room_id") REFERENCES "tb_chat_room"("chat_room_id"),
	PRIMARY KEY("message_id" AUTOINCREMENT),
	FOREIGN KEY("sender_employee_id") REFERENCES "tb_employee"("employee_id")
);

DROP TABLE IF EXISTS tb_medical_order;
CREATE TABLE "tb_medical_order" (
	"medical_order_id"	INTEGER,
	"patient_register_id"	INTEGER NOT NULL,
	"saved_timestamp"	TEXT NOT NULL,
	"order_statement"	TEXT NOT NULL,
	"recoder_doctor_id"	INTEGER NOT NULL,
	FOREIGN KEY("recoder_doctor_id") REFERENCES "tb_doctor"("doctor_id"),
	PRIMARY KEY("medical_order_id" AUTOINCREMENT)
);

INSERT INTO tb_employee("name", "type_job", "login_username", "login_password", "mobile_phone_num_1", "mobile_phone_num_2") values
("박광현", 2, "qqq", "1234", "010-1234-5678","-"),
("노우현", 1, "qq", "1234", "010-2222-5678","-"),
("주혜인", 2, "q123", "1234", "010-4422-5678","-");

INSERT INTO tb_chat_room("created_time") values ("2023-07-22 09:00:00"),("2023-07-21 21:00:00");
INSERT INTO tb_employee_chat_room("chat_room_id","employee_id") values (1, 1),
(1, 2),
(2, 1),
(2, 3);

INSERT INTO tb_message("sender_employee_id","chat_room_id","contents","is_confirmed") values
(1, 1, "ER 김철수님 NRS 7점 chest pain 호소합니다.", 0),
(1, 1, "처방주세요", 0),
(2, 1, "알겠습니다. 처방대로 주세요, 금방 가겠습니다.", 0),
(2, 2, "EDTA 보틀좀 빌려주세요", 0),
(1, 2, "우리도 없어요 죄송", 0);


INSERT INTO tb_nurse('employee_id', 'assigned_ward_id') values
(1, 9);

INSERT INTO tb_doctor('employee_id', 'assigned_department_id') values
(2, 3);

INSERT INTO tb_department("name", "job_category") VALUES
("응급의학과", 1),("신경외과", 1), ("흉부외과", 1), ("외과", 1), ("내과", 1), ("소아청소년과", 1),
("7A",2),("9B",2),("ER",2),("MICU",2),("101W",2),("EICU",2),
("원무과",3),("보험심사팀",3);

INSERT INTO tb_bed_of_ward("assigned_ward_id", "additional_info", "viewing_bed_name") VALUES
(9, "소생실", "CR1"),
(9, "소생실", "CR2"),
(9, "중증구역", "IC1"),
(9, "중증구역", "IC2"),
(9, "중증구역", "IC3"),
(9, "중증구역", "IC4"),
(9, "중증구역", "IC5"),
(9, "중증구역", "IC6"),
(9, "중증구역", "IC7"),
(9, "중증구역", "IC8"),
(9, "중증구역", "IC9"),
(9, "중증구역", "IC10"),
(9, "중증구역", "IC11"),
(9, "중증구역", "IC12"),
(9, "경증구역", "TR");

INSERT INTO tb_patient("birth_date", "name","sex", "ssn", "address", "type_insurance", "using_bed_id", "register_number", "assigned_doctor_id", "assigned_nurse_id") VALUES
('1958-06-08', '김철수', 'M', '580608-1354644', '경기도 수원시 언저리 어딘가', '건강보험', 1, 486124, 1, 1);

INSERT INTO tb_emergency_nurse_record(
    "onset_time",
    "register_id",
    "ktas_id",
    "cheif_complain",
    "description",
    "recorder_nurse_id",
    "saved_time",
    "responser",
    "memo") values
    ('2022-07-18',486124, 'AICAB', '가슴이 답답해요',
"내원전 2시간 전, 속 울렁거림, 가슴 답답함 느껴졌으나 시간 지나니 괜찮아짐, 식사 후 집으로 귀가하던 중 가슴 부여잡은 채 쓰러진 것 행인이 목격,\n 119 신고로 이송됨 이송 중에 의식 깨어 현재는 alert하나 흉통(NRS>7) 계속 호소중임",
1, "2023-07-20 13:11:22", "환자 본인", "- 보호자 대기(-) / 연락(+)\n- Chest Pain NRS 6점 -> noti(+) /처방(-)\n- ECG: ST Elevation r/o ischemic HF");

INSERT INTO tb_medical_order (
    "patient_register_id",
    "saved_timestamp",
    "order_statement",
    "recoder_doctor_id"
) values
(486124, "2023-07-18 02:23:44", "1. check v/s q 1hr", 1),
(486124, "2023-07-18 02:23:44", "2. restrict ABR", 1),
(486124, "2023-07-18 02:23:44", "3. Total NPO", 1),
(486124, "2023-07-18 02:23:44", "4. f/u BST q 6hr", 1),
(486124, "2023-07-18 02:23:44", "", 1),
(486124, "2023-07-18 02:23:44", "====== 수액 ========", 1),
(486124, "2023-07-18 02:23:44", "1. NS 1L/bag   100ml/hr", 1),
(486124, "2023-07-18 02:23:44", "", 1),
(486124, "2023-07-18 02:23:44", "====== PO ========", 1),
(486124, "2023-07-18 02:23:44", "1. Clopidrogel 300mg  6Tab", 1),
(486124, "2023-07-18 02:23:44", "2. Tigagreller 180mg 1Tab 둘 다 지금 복용해주세요", 1),
(486124, "2023-07-18 02:23:44", "", 1),
(486124, "2023-07-18 02:23:44", "====== Text Order =========", 1),
(486124, "2023-07-18 02:23:44", "보호자 대기 시켜주세요", 1),
(486124, "2023-07-18 02:23:44", "", 1),
(486124, "2023-07-18 02:23:44", "===== PCI Order ===========", 1),
(486124, "2023-07-18 02:23:44", "....", 1);
# client_connector.py
import socket
import traceback
from threading import Thread

from Common.class_common import Common
from Common.class_json_converter import ObjEncoder, ObjDecoder
from Domain.chat_room import ChatRoom
from Domain.dto.dto_class import DTOMaker
from Domain.message import Message
from Domain.people._employee import Employee


class Connector:
    def __init__(self):
        self.common = None
        self.client_socket = None
        self.receive_thread = None
        self.controller = None

        self.dto_maker = None
        self.login_employee = None
        self.selected_employee = None
        self.selected_patient = None
        self.selected_patient_medical_order = None
        self.selected_patient_emergency_nurse_record = None
        self.selected_patient_ktas = None
        self.patient_table_widget_list = None
        self.bed_id_list = None
        self.bed_name_list = None
        self.selected_chat_room = ChatRoom(None, None)
        self.is_keep_on_thread = True
        self.all_employee_list = list()
        self.selected_chat_room.employee_list = self.all_employee_list
        self.all_employee_department = list()

        self.encoder = ObjEncoder()
        self.decoder = ObjDecoder()

        self.setUp_attr()

    def setUp_attr(self):
        self.common = Common()
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client_socket.connect((self.common.HOST, self.common.PORT))
        self.client_socket.settimeout(5)
        self.client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.receive_thread = Thread(target=self.receive_message)
        self.receive_thread.daemon = True
        self.receive_thread.start()
        self.dto_maker = DTOMaker()

    def get_login_employee(self):
        return self.login_employee

    def get_selected_patient(self):
        return self.selected_patient

    def get_selected_chat_room(self):
        return self.selected_chat_room

    def set_selected_chat_room_by_employee_id(self, employee_id):
        pass

    def set_widget_controller(self, controller):
        # 위젯 연동
        self.controller = controller

    def send_login_dto(self, username, pw):
        dto = self.dto_maker.make_login_dto(username, pw)
        header = self.common.LOGIN_ACCESS_REQ
        data = self.encoder.toJSON_an_object(dto)
        self.send_message(header, data)

    def disconnect(self):
        self.client_socket: socket.socket
        self.client_socket.close()
        print('close 시도됨')
        self.is_keep_on_thread = False

    def receive_message(self):
        while True:
            if self.is_keep_on_thread is False:
                break

            try:
                print("Client_listening")
                recv_encoded_message = self.client_socket.recv(self.common.BUFFER)
                temp = recv_encoded_message.decode(self.common.FORMAT).strip()
                if len(temp) < 3:
                    continue
                response_header, response_data = self.header_data_distributor(temp)
                print(f"CLIENT RECEIVED{type(response_data)}: ({response_header},{response_data})")
            except OSError:
                continue
            except Exception:
                traceback.print_exc()
                continue

            # 로그인 응답 받음
            if response_header == self.common.LOGIN_ACCESS_RES:
                if response_data != self.common.FALSE:
                    self.login_employee = response_data  # employee obj
                self.controller.command_signal.emit(self.common.LOGIN_ACCESS_RES, response_data)
            # 환자 리스트 받음
            elif response_header == self.common.PATIENT_NAMELIST_RES:
                self.patient_table_widget_list = response_data
                # self.patient_list = [['IC-1'], "김철수(M/55)", "김순재/조운", "접수중"]
                self.controller.command_signal.emit(self.common.PATIENT_NAMELIST_RES, response_data)

            # 환자 정보 받음
            elif response_header == self.common.PATIENT_RES:
                self.selected_patient = response_data
                self.controller.command_signal.emit(self.common.PATIENT_RES, response_data)

            # 응급간호기록 받음
            elif response_header == self.common.EMERGENCY_NURSE_RECORD_RES:
                self.selected_patient_emergency_nurse_record = response_data
                self.controller.command_signal.emit(self.common.EMERGENCY_NURSE_RECORD_RES, response_data)

            # 직원 전체 기록 받음
            elif response_header == self.common.ALL_EMPLOYEE_LIST_RES:
                assert isinstance(response_data, list) and isinstance(response_data[0], Employee)
                self.all_employee_list.clear()
                self.all_employee_list.extend(response_data)

            # 직원 전체 부서 정보
            elif response_header == self.common.ALL_EMPLOYEE_DEPARTMENT_LIST_RES:
                assert isinstance(response_data, list)
                self.all_employee_department.clear()
                self.all_employee_department.extend(response_data)
                print("부서 정보 들어옴:", end="%%%%%%")
                print(self.all_employee_department)

            # 선택 채팅방 정보 받음
            elif response_header == self.common.CHAT_ROOM_RES:
                assert isinstance(response_data, ChatRoom)
                self.selected_chat_room.chat_room_id = response_data.chat_room_id
                self.controller.command_signal.emit(self.common.CHAT_ROOM_RES, response_data)

            # 메시지 리스트 받음
            elif response_header == self.common.MESSAGE_LIST_RES:
                assert isinstance(response_data, list) and isinstance(response_data[0], Message)
                self.selected_chat_room.append_or_extend_message(response_data)
                self.controller.command_signal.emit(self.common.SEND_A_MESSAGE_RES, '')

            # 단발성 메시지 수신
            elif response_header == self.common.SEND_A_MESSAGE_RES:
                assert isinstance(response_data, Message)
                arrival_message = response_data
                self.selected_chat_room.append_or_extend_message(arrival_message)
                self.controller.command_signal.emit(self.common.SEND_A_MESSAGE_RES, arrival_message)

    def send_message(self, header, data):
        assert isinstance(header, str) or isinstance(header, bytes)
        assert isinstance(data, str) or isinstance(data, bytes)
        if isinstance(header, bytes):
            header_to_sending = header.decode(self.common.FORMAT)
        else:
            header_to_sending = header
        if isinstance(data, bytes):
            data_to_sending = data.decode(self.common.FORMAT)
        else:
            data_to_sending = data

        message_to_send = f"{self.common.START_OF_TEXT}".join([header_to_sending, data_to_sending]) \
            .encode(self.common.FORMAT)

        if len(message_to_send) >= self.common.BUFFER:
            raise "부족한 버퍼 용량"
        print(f"CLIENT SENDED: (HEADER: {header} | DATA: {data})")
        self.client_socket.send(message_to_send)

    def header_data_distributor(self, decoded_message):
        # decoded_message = recv_encoded_message.decode(self.common.FORMAT).strip()
        header, data_decoded_str, = decoded_message.split(self.common.START_OF_TEXT)
        data = self.decoder.binary_to_obj(data_decoded_str)
        return header, data

    def send_chat_room_request_with_employee(self, employee: Employee):
        self.selected_employee = employee
        data = f"{(self.login_employee.employee_id, employee.employee_id)}"
        self.send_message(self.common.CHAT_ROOM_REQ, f"{data}" )

    def send_msg_to_server(self, msg_str):
        new_msg = Message(None, self.login_employee.employee_id, self.selected_chat_room.chat_room_id, msg_str, False)
        data = self.encoder.toJSON_an_object(new_msg)
        self.send_message(self.common.SEND_A_MESSAGE_REQ, data)

    def get_messages_as_string(self, selected_chat_room_id=None):
        msg_list = self.selected_chat_room.message_list
        selected_msg_list = [x for x in msg_list if x.chat_room_id == selected_chat_room_id]
        result_list = list()
        for m in selected_msg_list:
            m:Message
            if m.sender_employee_id == self.login_employee.employee_id:
                sender_name = f"{self.login_employee.name}(나)"
            else:
                sender_name = f"{self.selected_employee.name}(상대)"
            a_line = f"{sender_name} >> {m.contents}"
            result_list.append(a_line)
        return result_list
# client_controller.py
import datetime
import time

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import QPoint, Qt, pyqtSignal
from PyQt5.QtWidgets import QMessageBox

from Client.class_widget_admin import WidgetAdmin
from Client.class_widget_chat_room import WidgetChatRoom
from Client.class_widget_dashboard import WidgetDashBoard
from Client.class_widget_employee_finder import WidgetEmployeeFinder
from Client.class_widget_login import WidgetLogin
from Client.class_widget_medical import WidgetMedical
from Client.client_connector import Connector
from Common.class_common import Common
from Domain.people._employee import Employee


class WidgetController(QtWidgets.QWidget):
    command_signal = pyqtSignal(str, object)

    def __init__(self, connector=Connector):
        assert isinstance(connector, Connector)
        super().__init__()
        self.connector = connector  # client 연결
        self.connector.set_widget_controller(self)
        self.widget_admin = None
        self.widget_chat_room = None
        self.widget_dashboard = None
        self.widget_e_finder = None
        self.widget_login = None
        self.widget_medical = None
        self.common = None
        self.setUp()

    def login_access(self, username, pw):
        self.connector.send_login_dto(username, pw)

    def run(self):
        self.show()


    def reset(self):
        pass

    def setUp(self):
        self.common = Common()
        self.set_up_widgets()
        self.set_up_triggers()

    def disconnect_server(self):
        self.connector.disconnect()

    def show(self):
        # self.widget_admin.show()
        # self.widget_chat_room.show()
        # self.widget_dashboard.show()
        # self.widget_e_finder.show()
        self.widget_login.show()
        # self.widget_medical.show()

    def set_up_widgets(self):
        self.widget_admin = WidgetAdmin(self)
        self.widget_chat_room = WidgetChatRoom(self)
        self.widget_dashboard = WidgetDashBoard(self)
        self.widget_e_finder = WidgetEmployeeFinder(self)
        self.widget_login = WidgetLogin(self)
        self.widget_medical = WidgetMedical(self)

    def set_up_triggers(self):
        self.command_signal.connect(self.signal_handler)

    def get_all_employee(self):
        return self.connector.all_employee_list
    def get_patient_list(self):
        return self.connector.patient_table_widget_list

    def signal_handler(self, command_str, return_data_obj):
        if command_str == self.common.LOGIN_ACCESS_RES:
            if return_data_obj != self.common.FALSE:
                self.show_success_login()
            else:
                return QMessageBox.about(self.widget_login, "로그인 실패", "로그인 정보를 다시 확인해주세요.")
        elif command_str == self.common.PATIENT_NAMELIST_RES:
            self.widget_medical.refresh_patient_list_table_view(return_data_obj)
        elif command_str == self.common.PATIENT_RES:
            self.widget_medical.refresh_patient_info(return_data_obj)
        elif command_str == self.common.EMERGENCY_NURSE_RECORD_RES:
            self.widget_medical.refresh_emergency_nurse_record_info(return_data_obj)
        elif command_str == self.common.CHAT_ROOM_RES:
            self.widget_chat_room.close()
            self.widget_chat_room.show()
        elif command_str == self.common.SEND_A_MESSAGE_RES:
            self.refresh_chat_room_plain_text()

    def open_chat_room_with_employee(self, employee:Employee):
        self.connector.send_chat_room_request_with_employee(employee)
        self.widget_chat_room:WidgetChatRoom
        self.widget_chat_room.show()

    def send_message_to_connect(self, msg_str):
        self.connector.send_msg_to_server(msg_str)

    def refresh_chat_room_plain_text(self):
        self.widget_chat_room.refresh_plain_text()
    def show_success_login(self):
        # login 창 닫기
        self.widget_login.close()
        # 윈도우 타이틀 설정
        employee = self.connector.get_login_employee()
        self.widget_medical: WidgetMedical
        if employee.type_job == 1:
            title = self.widget_medical.windowTitle() + " --- " + employee.name + '의사 의료통합정보 체계 로그인'
            self.widget_medical.setWindowTitle(title)
            self.widget_medical.show()
        elif employee.type_job == 2:
            title = self.widget_medical.windowTitle() + " --- " + employee.name + '간호사 의료통합정보 체계 로그인'
            self.widget_medical.setWindowTitle(title)
            self.widget_medical.show()
        else:
            title = self.widget_admin.windowTitle() + " --- " + employee.name + '의료통합정보 체계 로그인'
            self.widget_admin.setWindowTitle(title)
            self.widget_admin.show()

    @staticmethod
    def clear_widget(widget):
        if widget.layout() is not None:
            while widget.layout().count() > 0:
                item = widget.layout().takeAt(0)
                if item.widget():
                    item.widget().deleteLater()


    def show_employee_finder_widget(self):
        self.widget_e_finder.show()

    def show_chat_room_widget(self):
        self.widget_chat_room.show()
# common.py
from datetime import datetime

from PyQt5 import QtWidgets


class Common:
    _instance = None

    # START_OF_HEADER = chr(1)
    START_OF_TEXT = chr(2)
    # END_OF_TEXT = chr(3)

    HOST = '127.0.0.1'
    PORT = 9999
    BUFFER = 50000
    FORMAT = "utf-8"

    LOGIN_ACCESS_REQ = "login_access_request"
    LOGIN_ACCESS_RES = "login_access_response"
    PATIENT_NAMELIST_REQ = "patient_name_list_request"
    PATIENT_NAMELIST_RES = "patient_name_list_response"
    PATIENT_REQ = "patient_request"
    PATIENT_RES = "patient_response"
    EMERGENCY_NURSE_RECORD_REQ = "emergency_nurse_record_request"
    EMERGENCY_NURSE_RECORD_RES = "emergency_nurse_record_response"
    MEDICAL_ORDER_REQ = "medical_order_request"
    MEDICAL_ORDER_RES = "medical_order_response"
    KTAS_REQ = "ktas_request"
    KTAS_RES = "ktas_response"
    CHAT_ROOM_REQ = "chat_room_request"
    CHAT_ROOM_RES = "chat_room_response"
    ALL_EMPLOYEE_LIST_REQ = "all_employee_list_request"
    ALL_EMPLOYEE_LIST_RES = "all_employee_list_response"
    ALL_EMPLOYEE_DEPARTMENT_LIST_RES = "all_employee_department_list_response"
    ALL_EMPLOYEE_DEPARTMENT_LIST_REQ = "all_employee_department_list_request"
    SEND_A_MESSAGE_REQ = "send_a_message_request"
    SEND_A_MESSAGE_RES = "send_a_message_response"
    MESSAGE_LIST_RES = "message_list_response"
    TRUE = "True"
    FALSE = "False"

    def __new__(cls):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self):
        pass

    @staticmethod
    def show_error_message(message, traceback):
        msg_box = QtWidgets.QMessageBox()
        msg_box.setIcon(QtWidgets.QMessageBox.Critical)
        msg_box.setWindowTitle("Error")
        msg_box.setText(message)
        msg_box.exec_()
        traceback.print_exc()

    @staticmethod
    def get_subtract_time(str_start_timestamp):
        try:
            parsed_datetime = datetime.strptime(str_start_timestamp, "%Y-%m-%d %H:%M:%S")
        except:
            return '알수 없는 시간'
        now_time = datetime.now()
        result_time_delta = now_time - parsed_datetime
        return f"{result_time_delta.min}"
    @staticmethod
    def get_now_time_str():
        return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# json_converter.py
import json
import traceback

from Domain.chat_room import ChatRoom
from Domain.department import Department
from Domain.emergency_nurse_record import EmergencyNurseRecord
from Domain.ktas import KTAS
from Domain.message import Message
from Domain.people._employee import Employee
from Domain.people.patient import Patient


class ObjEncoder(json.JSONEncoder):
    _instance = None

    def __new__(cls):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self):
        super().__init__()

    def toJSON_as_binary(self, obj):
        if isinstance(obj, list):
            temp_list = list()
            for o in obj:
                str_obj = self.toJSON_an_object(o)
                temp_list.append(str_obj)
            list_json = json.dumps(temp_list, default=lambda o: o.__dict__)
            return list_json.encode('utf-8')
        return self.toJSON_an_object_with_encode(obj)

    def toJSON_an_object_with_encode(self, obj):
        json_string = self.toJSON_an_object(obj)
        return json_string.encode('utf-8')

    def toJSON_an_object(self, obj):
        if isinstance(obj, list):
            result_list = []
            for o_ in obj:
                string_converted_o_ = json.dumps(o_, default=lambda o: o.__dict__)
                result_list.append(string_converted_o_)
            return self.encode(result_list)
        if isinstance(obj, ChatRoom):
            origin_dict = obj.to_dict_for_insert()
            string_converted_obj = json.dumps(origin_dict)
        else:
            string_converted_obj = json.dumps(obj, default=lambda o: o.__dict__)
        json_string = self.encode(string_converted_obj)
        return json_string


class ObjDecoder(json.JSONDecoder):
    _instance = None

    def __new__(cls):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self):
        super().__init__()

    def binary_to_obj(self, binary_str):
        # object 대신 TRUE or FALSE 로 대답해주는 경우엔 그대로 전송되게 함
        if binary_str == "True":
            return binary_str
        elif binary_str == "False":
            return binary_str

        if isinstance(binary_str, bytes):  # binary -> utf-8
            binary_str = binary_str.decode('utf-8')

        if "'" in binary_str:
            binary_str = binary_str.replace("'", '"')

        # 리스트 내 튜플 타입 처리
        if binary_str.startswith("[("):
            result = self.tuple_in_list_convert(binary_str)
            return result
        if binary_str.startswith('"["{"'):
            result = self.dict_in_list_convert(binary_str)
            return result
        if binary_str.startswith("("):
            result = self.tuple_str_convert(binary_str)
            return result

        json_string = json.loads(binary_str)  # utf-8 -> json

        if isinstance(json_string, list):
            result_obj = self.list_mapper(json_string)
        else:
            result_obj = self.object_mapper(json_string)
        if result_obj is not None:
            return result_obj
        if isinstance(json_string, str):
            json_string = json.loads(json_string)
        return json_string

        # json_to_object = json.loads(json_string)  # json -> dict(default)

    def dict_in_list_convert(self, tuple_in_list):
        dictionaries = tuple_in_list.replace('"["', "").replace('"]"', "").split("}, {")
        str_dict_list = list()
        for d_insufficient in dictionaries:
            if not d_insufficient.startswith("{"):
                d_insufficient = "{" + d_insufficient
            if not d_insufficient.endswith("}"):
                d_insufficient = d_insufficient + "}"
            d_sufficient = d_insufficient
            str_dict_list.append(d_sufficient)
        result_list = self.list_mapper(str_dict_list)
        return result_list


    def tuple_str_convert(self, tuple_str):
        temp_list = tuple_str[1:len(tuple_str) - 1].replace(" ", "").replace("'", '').split(',')
        result_list = list()
        for item in temp_list:
            if item.isdigit():
                result_list.append(int(item))
            else:
                result_list.append(item)
        return tuple(result_list)

    def tuple_in_list_convert(self, double_list_str):
        temp_list = double_list_str[1:len(double_list_str) - 1].replace(" ", "").replace("'", '').split('),(')
        result_list = list()
        for row in temp_list:
            if row.startswith('('):
                row = row[1:]
            if row.endswith(')'):
                row = row[:len(row) - 1]
            str_list = row.split(',')
            result_list.append(tuple(str_list))
        return result_list

    def object_mapper(self, dict_obj):

        if "'" in dict_obj:
            dict_obj = dict_obj.replace("'", '"')
        try:
            if isinstance(dict_obj, str):
                dict_obj = json.loads(dict_obj)
        except:
            traceback.print_exc()
            print(f"object_mapper 불가<type:{type(dict_obj)}>:{dict_obj}")

        if isinstance(dict_obj, str):
            dict_obj = json.loads(dict_obj)
        assert isinstance(dict_obj, dict)
        if "is_confirmed" in dict_obj.keys():
            return Message(**dict_obj)
        elif "mobile_phone_num_1" in dict_obj.keys():
            return Employee(**dict_obj)
        elif "chat_room_id" in dict_obj.keys() and "created_time" in dict_obj.keys():
            return ChatRoom(**dict_obj)
        elif "first_category_name" in dict_obj.keys():
            return KTAS(**dict_obj)
        elif 'enr_id' in dict_obj.keys():
            return EmergencyNurseRecord(**dict_obj)
        elif 'department_id' in dict_obj.keys() and 'job_category' in dict_obj.keys():
            return Department(**dict_obj)
        elif 'patient_id' in dict_obj.keys() and 'birth_date' in dict_obj.keys():
            return Patient(**dict_obj)

    def list_mapper(self, list_obj):
        assert isinstance(list_obj, list)
        result_list = list()
        try:
            for o in list_obj:
                converted_o = self.object_mapper(o)
                result_list.append(converted_o)
        except:
            print(f"list 객체화 불가 : {list_obj}")
            result_list = list_obj
        return result_list
# fake_data_maker.py
import random
import datetime

from faker import Faker
import numpy as np


class FakeDataMaker:
    def __init__(self):
        self.faker = Faker("ko-KR")

    @staticmethod
    def get_random_temp():
        bt = int(np.random.normal(scale=4) + 370)
        return bt

    @staticmethod
    def get_random_hr():
        hr = int(np.random.normal(scale=10) + 80)
        return hr

    @staticmethod
    def get_random_rr():
        rr = int(np.random.normal(scale=4) + 16)
        return rr

    @staticmethod
    def get_random_register_num_list(size):
        result_list = list()
        while len(result_list) < size:
            rand_num = f"{int(random.random() * (10 ** 6)):06d}"
            if rand_num not in result_list:
                result_list.append(rand_num)
        return result_list

    @staticmethod
    def get_random_back_ssn_num_list(option_: str):
        if option_ == 'young':
            gender = random.randint(3, 5)
        else:
            gender = random.randint(1, 3)
        back_num = f'{int(random.random() * (10 ** 6)):06d}'
        return f'{gender}{back_num}'

    @staticmethod
    def get_random_date_time():
        date = datetime.datetime.strptime("2022-01-01 00:00:00", "%Y-%m-%d %H:%M:%S")
        random_hour = random.randint(1, 1000)
        temp_date = date + datetime.timedelta(hours=random_hour)
        return temp_date

    def get_random_date_time_str(self):
        return self.get_random_date_time().strftime("%Y-%m-%d %H:%M:%S")

    def get_random_fake_name(self):
        return self.faker.name()

    @staticmethod
    def get_random_type_job():
        return random.randint(1, 3)
        # if num == 1:
        #     return "nurse"
        # elif num == 2:
        #     return "doctor"
        # elif num == 3:
        #     return "admin"
        # print(num)
        # raise 'randint 확인'

    def get_random_phone_num(self):
        return self.faker.phone_number()

    def get_random_time(self) -> str:
        time_str = self.faker.time()
        return time_str

    def get_random_login_id(self) -> str:
        simple_profile = self.faker.simple_profile()
        return simple_profile['username']

    def get_random_simple_profile(self) -> dict:
        # {'username': 'jinyeongsug', 'name': '이현우', 'sex': 'M',
        # 'address': '경상북도 광명시 역삼거리 (영미김이읍)',
        # 'mail': 'eungyeong71@dreamwiz.com', 'birthdate': datetime.date(1912, 12, 10)}
        return self.faker.simple_profile()

    def get_random_ssn(self) -> str:
        return self.faker.ssn()

    def get_random_lorem(self) -> str:
        return self.faker.catch_phrase()
# insert_data_to_tables.py
import random
import re
import sqlite3

from Back.db_connector import DBConnector
from Domain.chat_room import ChatRoom
from Domain.ktas import KTAS
from Domain.message import Message
from Domain.people._employee import Employee
from Common.fake_data_maker import FakeDataMaker


def insert_KTAS_data(conn):
    second_category_name_set = set()
    third_category_name_set = set()
    fourth_category_name_set = set()

    whole_data = None

    with open("../data/emergency_grade.txt", 'r', encoding='utf-8') as file:
        whole_data = file.read()

    if whole_data is None:
        raise '파일 못읽음'

    lines = whole_data.split('\n')

    result_list = list()
    for row in lines:
        word_list = row.split(' ')
        temp_list = list()

        slice_index = -1
        for i, w in enumerate(word_list):
            if len(w) == 1:
                slice_index = i
                break
        for w in word_list[:slice_index]:
            temp_list.append(word_list.pop(0))

        if slice_index == -1:
            raise '못자르고 있음'

        second_category_name = ','.join(temp_list)
        second_category_name_set.add(second_category_name)  ## 세트 추가
        second_category_code = word_list.pop(0)
        temp_list.clear()

        while not re.match(r"[A-Z]", word_list[0]):
            temp_list.append(word_list.pop(0))
        third_category_name = ' '.join(temp_list)
        third_category_name_set.add(third_category_name)  ## 세트 추가
        third_category_code = word_list.pop(0)
        temp_list.clear()

        while not re.match(r"[A-Z]{2}", word_list[0]):
            temp_list.append(word_list.pop(0))

        fourth_category_name = ' '.join(temp_list)
        fourth_category_name_set.add(fourth_category_name)  ## 세트 추가
        fourth_category_code = word_list.pop(0)
        temp_list.clear()

        final_grade = int(word_list.pop())
        ktas_code = ''.join(['A', second_category_code, third_category_code, fourth_category_code])
        result_list.append(
            KTAS('성인', 'A', second_category_name, second_category_code, third_category_name, third_category_code,
                 fourth_category_name, fourth_category_code, final_grade, ktas_code))

    with open("../data/child_grade.txt", 'r', encoding='utf-8') as file:
        whole_data = file.read()

    if whole_data is None:
        raise '파일 못읽음'

    lines = whole_data.split('\n')

    for row in lines:
        word_list = row.split(' ')
        temp_list = list()

        slice_index = -1
        for i, w in enumerate(word_list):
            if len(w) == 1:
                slice_index = i
                break
        for w in word_list[:slice_index]:
            temp_list.append(word_list.pop(0))

        if slice_index == -1:
            raise '못자르고 있음'

        second_category_name = ','.join(temp_list)
        second_category_name_set.add(second_category_name)  ## 세트 추가
        second_category_code = word_list.pop(0)
        temp_list.clear()

        while not re.match(r"[A-Z]", word_list[0]):
            temp_list.append(word_list.pop(0))
        third_category_name = ' '.join(temp_list)
        third_category_name_set.add(third_category_name)  ## 세트 추가
        third_category_code = word_list.pop(0)
        temp_list.clear()

        while not re.match(r"[A-Z]{2}", word_list[0]):
            temp_list.append(word_list.pop(0))

        fourth_category_name = ' '.join(temp_list)
        fourth_category_name_set.add(fourth_category_name)  ## 세트 추가
        fourth_category_code = word_list.pop(0)
        temp_list.clear()

        final_grade = int(word_list.pop())
        ktas_code = ''.join(['B', second_category_code, third_category_code, fourth_category_code])

        result_list.append(
            KTAS('소아', 'B', second_category_name, second_category_code, third_category_name, third_category_code,
                 fourth_category_name, fourth_category_code, final_grade, ktas_code))

    c = conn.start_conn()
    # first_category_name
    # first_category_code
    # second_category_name
    # second_category_code
    # third_category_name
    # third_category_code
    # fourth_category_name
    # fourth_category_code
    # final_grade
    for i in result_list:
        c.execute('''INSERT INTO tb_ktas (first_category_name, first_category_code, second_category_name,
         second_category_code, third_category_name, third_category_code,
         fourth_category_name, fourth_category_code, final_grade, ktas_code)
         VALUES (
         :first_category_name, 
         :first_category_code, 
         :second_category_name,
         :second_category_code,
         :third_category_name, 
         :third_category_code, 
         :fourth_category_name,
         :fourth_category_code,
         :final_grade,
         :ktas_code);''', i.__dict__)
    conn.commit_db()
    conn.end_conn()

def insert_dummy_employee_data(conn: DBConnector, size):

    conn.insert_employee(Employee(None, "이승신", 2, "qwer11", "1234", "010-1010-0102", "010-0215-1335"))
    conn.insert_employee(Employee(None, "호후현", 3, "qwer13", "1234", "010-1010-0102", "010-0215-1335"))
    maker = FakeDataMaker()
    for i in range(size):
        # 가짜 생성
        name = maker.get_random_fake_name()
        type_job = maker.get_random_type_job()
        login_username = maker.get_random_login_id()
        login_pw = "1234"
        phone_1 = maker.get_random_phone_num()
        phone_2 = maker.get_random_phone_num()
        # 객체화 및 입력
        dummy_employee = Employee(None, name, type_job, login_username, login_pw, phone_1, phone_2)
        dummy_employee = conn.insert_employee(dummy_employee)
        if dummy_employee.type_job == 1:
            conn.insert_doctor_job(dummy_employee, random.randint(1, 6))
        elif dummy_employee.type_job == 2:
            conn.insert_nurse_job(dummy_employee, random.randint(7, 12))
        elif dummy_employee.type_job == 3:
            conn.insert_admin_job(dummy_employee, random.randint(13, 14))
def insert_dummy_chat_room(conn: DBConnector, size):
    maker = FakeDataMaker()
    for i in range(size):
        # 가짜 생성
        # 객체화 및 입력
        random_date = maker.get_random_date_time_str()
        dummy_chat_room = ChatRoom(None, random_date)
        conn.insert_chat_room(dummy_chat_room)


def insert_dummy_employee_chat_room_data(conn: DBConnector, size, employee_range, chat_range):
    maker = FakeDataMaker()
    min_id_employee, max_id_employee = employee_range
    min_id_chat, max_id_chat = chat_range
    for i in range(size):
        # 가짜 생성
        # 객체화 및 입력
        random_employee_id_1, random_employee_id_2 = random.sample(range(min_id_employee, max_id_employee), 2)
        random_chat_room_id = random.randint(min_id_chat, max_id_chat)
        conn.insert_employee_chat_room(random_employee_id_1, random_chat_room_id)
        conn.insert_employee_chat_room(random_employee_id_2, random_chat_room_id)
        for _ in range(random.randint(1, 10)):
            # 가짜 생성
            # 객체화 및 입력
            dummy_message_1 = Message(None, random_employee_id_1, random_chat_room_id, maker.get_random_lorem(), False)
            dummy_message_2 = Message(None, random_employee_id_2, random_chat_room_id, maker.get_random_lorem(), False)
            conn.insert_message(dummy_message_1)
            conn.insert_message(dummy_message_2)


def insert_dummy_message_data(conn: DBConnector, size, employee_range, chat_range):
    maker = FakeDataMaker()
    min_id_employee, max_id_employee = employee_range
    min_id_chat, max_id_chat = chat_range
    for i in range(size):
        # 가짜 생성
        # 객체화 및 입력
        random_employee_id = random.randint(4, max_id_employee)
        random_chat_room_id = random.randint(4, max_id_chat)
        dummy_message = Message(None, random_employee_id, random_chat_room_id, maker.get_random_lorem(), False)
        conn.insert_message(dummy_message)
# employee.py
class Employee:
    def __init__(self, employee_id, name, type_job, login_username, login_password,
                 mobile_phone_num_1, mobile_phone_num_2):
        self.employee_id = employee_id
        self.name = name
        self.type_job = type_job
        self.login_username = login_username
        self.login_password = login_password
        self.mobile_phone_num_1 = mobile_phone_num_1
        self.mobile_phone_num_2 = mobile_phone_num_2

    def __str__(self):
        return self.__repr__()

    def __repr__(self):
        return f"{self.__dict__}"

    def copy(self):
        return Employee(self.employee_id, self.name, self.type_job, self.login_username, self.login_password, self.mobile_phone_num_1, self.mobile_phone_num_2)

    def get_job(self):
        if self.type_job == 1:
            return "의사"
        elif self.type_job == 2:
            return "간호사"
        elif self.type_job == 3:
            return "행정 직원"
        raise "type job 확인 요망"
# employee.py
class Employee:
    def __init__(self, employee_id, name, type_job, login_username, login_password,
                 mobile_phone_num_1, mobile_phone_num_2):
        self.employee_id = employee_id
        self.name = name
        self.type_job = type_job
        self.login_username = login_username
        self.login_password = login_password
        self.mobile_phone_num_1 = mobile_phone_num_1
        self.mobile_phone_num_2 = mobile_phone_num_2

    def __str__(self):
        return self.__repr__()

    def __repr__(self):
        return f"{self.__dict__}"

    def copy(self):
        return Employee(self.employee_id, self.name, self.type_job, self.login_username, self.login_password, self.mobile_phone_num_1, self.mobile_phone_num_2)

    def get_job(self):
        if self.type_job == 1:
            return "의사"
        elif self.type_job == 2:
            return "간호사"
        elif self.type_job == 3:
            return "행정 직원"
        raise "type job 확인 요망"
# pathient.py
import datetime


class Patient:
    def __init__(self, patient_id, birth_date, name, sex, ssn, address,
                 type_insurance, using_bed_id,
                 register_number, assigned_doctor_id, assigned_nurse_id):
        self.patient_id = patient_id
        self.birth_date = birth_date
        self.name = name
        self.sex = sex
        self.ssn = ssn
        self.address = address
        self.type_insurance = type_insurance
        self.using_bed_id = using_bed_id
        self.register_number = register_number
        self.assigned_doctor_id = assigned_doctor_id
        self.assigned_nurse_id = assigned_nurse_id

    def __str__(self):
        return self.__repr__()

    def __repr__(self):
        return f"{self.__dict__}"

    def get_age(self):
        live_length = datetime.datetime.now() - datetime.datetime.strptime(self.birth_date, "%Y-%m-%d")
        age = live_length.days // 365
        return age

    def get_name_and_age(self):
        return f"{self.name}({self.sex}/{self.get_age()})"
# chatroom.py
import datetime

from Domain.message import Message
from Domain.people._employee import Employee


class ChatRoom:
    def __init__(self, chat_room_id, created_time=None, employee_list=None, message_list=None):
        self.chat_room_id = chat_room_id
        if created_time is not None:
            self.created_time = self.date_time_converter(created_time)
        else:
            self.created_time = created_time
        if employee_list is not None: # 리스트 초기화
            self.employee_list = employee_list
        else:
            self.employee_list = list()

        if message_list is not None:
            self.message_list = message_list
        else:
            self.message_list = list()

    def append_or_extend_message(self, value):
        if isinstance(value, list):
            assert isinstance(value[0], Message)
            for m in value:
                if m not in self.message_list:
                    self.message_list.append(m)
        else:
            assert isinstance(value, Message)
            if value not in self.message_list:
                self.message_list.append(value)

    def get_messages(self):
        result_message_list = [x for x in self.message_list if x.chat_room_id == self.chat_room_id]
        return result_message_list






    def get_date_str(self, format_=None):
        if format_ is None:
            return self.created_time.strftime('%Y-%m-%d %H:%M:%S')
        else:
            return self.created_time.strftime(format_)

    def __str__(self):
        return self.__repr__()

    def __repr__(self):
        return f"{self.__dict__}"

    def to_dict_for_insert(self):
        origin_dict = self.__dict__.copy()
        origin_dict['created_time'] = self.created_time.strftime('%Y-%m-%d %H:%M:%S')
        if self.chat_room_id is None:
            del origin_dict['chat_room_id']
        del origin_dict['employee_list']
        del origin_dict['message_list']
        return origin_dict


    @staticmethod
    def date_time_converter(created_time):
        if isinstance(created_time, str):
            result = datetime.datetime.strptime(created_time, '%Y-%m-%d %H:%M:%S')
            return result
        if isinstance(created_time, datetime.datetime):
            return created_time
        else:
            raise 'datetime 확인불가'
# message.py
import json


class Message:
    def __init__(self, message_id, sender_employee_id, chat_room_id, contents, is_confirmed):
        self.assert_param(message_id,
                          sender_employee_id,
                          chat_room_id,
                          contents,
                          is_confirmed)
        self.message_id = message_id
        self.sender_employee_id = sender_employee_id
        self.chat_room_id = chat_room_id
        self.contents = contents
        self.is_confirmed = self.bool_definer(is_confirmed)

    @staticmethod
    def assert_param(message_id,
                     sender_employee_id,
                     chat_room_id,
                     contents,
                     is_confirmed):
        assert isinstance(message_id, int) or message_id is None
        assert isinstance(sender_employee_id, int)
        assert isinstance(chat_room_id, int)
        assert isinstance(contents, str)
        assert isinstance(is_confirmed, bool) or isinstance(is_confirmed, int)

    @staticmethod
    def bool_definer(value_):
        if isinstance(value_, bool):
            return value_
        if value_ == 1:
            return True
        elif value_ == 0:
            return False 
        else:
            raise 'bool값 확인요망'


    def to_dict_for_insert(self):
        origin_dict = self.__dict__.copy()
        if self.is_confirmed is True:
            origin_dict['is_confirmed'] = 1
        else:
            origin_dict['is_confirmed'] = 0
        return origin_dict

    def __eq__(self, other):
        if isinstance(other, self.__class__) and self.message_id == other.message_id:
            return True
        return False

    def __lt__(self, other):
        return self.message_id < other.message_id

    def __str__(self):
        return self.__repr__()

    def __repr__(self):
        return f"{self.__dict__}"
# test_client_launcher.py
import sys

from PyQt5.QtWidgets import QApplication

from Client.client_connector import Connector
from Client.client_controller import WidgetController
from Common.class_common import Common

if __name__ == '__main__':
    common = Common()
    app = QApplication(sys.argv)
    client_connector = Connector()
    client_controller = WidgetController(client_connector)
    client_controller.run()
    sys.excepthook = lambda exctype, value, traceback: common.show_error_message(str(value), traceback)
    sys.exit(app.exec_())

 

오늘 목표 한 일 : 테이블 관계 수정 및 SQL 수정, UI 위젯 기능 구현, 실시간 채팅 구현

실제 한 일 : 테이블 관계 수정 및 SQL 수정, UI 위젯 기능 구현, 실시간 채팅 일부 구현

 


 

 

 

 

 

 


 

개발이슈 : 적당히 파싱하여 object는 object대로, 튜플은 튜플대로, 리스트(튜플)구조는 그 구조대로 만들어주는 리스트 만들기

 

이럴 의도로 만든게 아닌데.. 점점 json converter의 기능이 복잡해지고 길어지고 있다.

import json

from Domain.department import Department
from Domain.emergency_nurse_record import EmergencyNurseRecord
from Domain.ktas import KTAS
from Domain.message import Message
from Domain.people._employee import Employee
from Domain.people.patient import Patient


class ObjEncoder(json.JSONEncoder):
    _instance = None

    def __new__(cls):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self):
        super().__init__()

    def toJSON_as_binary(self, obj):
        if isinstance(obj, list):
            temp_list = list()
            for o in obj:
                str_obj = self.toJSON_an_object(o)
                temp_list.append(str_obj)
            list_json = json.dumps(temp_list, default=lambda o: o.__dict__)
            return list_json.encode('utf-8')
        return self.toJSON_an_object_with_encode(obj)

    def toJSON_an_object_with_encode(self, obj):
        json_string = self.toJSON_an_object(obj)
        return json_string.encode('utf-8')

    def toJSON_an_object(self, obj):
        print(obj.__dict__)
        string_converted_obj = json.dumps(obj, default=lambda o: o.__dict__)
        json_string = self.encode(string_converted_obj)
        return json_string


class ObjDecoder(json.JSONDecoder):
    _instance = None

    def __new__(cls):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self):
        super().__init__()

    def binary_to_obj(self, binary_str):
        # object 대신 TRUE or FALSE 로 대답해주는 경우엔 그대로 전송되게 함
        if binary_str == "True":
            return binary_str
        elif binary_str == "False":
            return binary_str

        if isinstance(binary_str, bytes):  # binary -> utf-8
            binary_str = binary_str.decode('utf-8')

        # 리스트 내 튜플 타입 처리
        if binary_str.startswith("[("):
            result = self.list_str_convert(binary_str)
            return result
        if binary_str.startswith("("):
            result = self.tuple_str_convert(binary_str)
            return result
        if "'" in binary_str:
            binary_str = binary_str.replace("'", '"')
        json_string = json.loads(binary_str)  # utf-8 -> json

        if isinstance(json_string, list):
            result_obj = self.list_mapper(json_string)
        else:
            result_obj = self.object_mapper(json_string)
        if result_obj is not None:
            return result_obj
        if isinstance(json_string, str):
            json_string = json.loads(json_string)
        return json_string

        # json_to_object = json.loads(json_string)  # json -> dict(default)

    def tuple_str_convert(self, tuple_str):
        temp_list = tuple_str[1:len(tuple_str)-1].replace(" ", "").replace("'", '').split(',')
        result_list = list()
        for item in temp_list:
            if item.isdigit():
                result_list.append(int(item))
            else:
                result_list.append(item)
        return tuple(result_list)


    def list_str_convert(self, double_list_str):
        temp_list = double_list_str[1:len(double_list_str) - 1].replace(" ", "").replace("'", '').split('),(')
        result_list = list()
        for row in temp_list:
            if row.startswith('('):
                row = row[1:]
            if row.endswith(')'):
                row = row[:len(row)-1]
            str_list = row.split(',')
            result_list.append(tuple(str_list))
        return result_list

    def object_mapper(self, dict_obj):
        if isinstance(dict_obj, str):
            dict_obj = json.loads(dict_obj)
        if isinstance(dict_obj, str):
            dict_obj = json.loads(dict_obj)
        assert isinstance(dict_obj, dict)
        if "is_confirmed" in dict_obj.keys():
            return Message(**dict_obj)
        elif "mobile_phone_num_1" in dict_obj.keys():
            return Employee(**dict_obj)
        elif "first_category_name" in dict_obj.keys():
            return KTAS(**dict_obj)
        elif 'enr_id' in dict_obj.keys():
            return EmergencyNurseRecord(**dict_obj)
        elif 'department_id' in dict_obj.keys() and 'job_category' in dict_obj.keys():
            return Department(**dict_obj)
        elif 'patient_id' in dict_obj.keys() and 'birth_date' in dict_obj.keys():
            return Patient(**dict_obj)

    def list_mapper(self, list_obj):
        assert isinstance(list_obj, list)
        result_list = list()
        try:
            for o in list_obj:
                converted_o = self.object_mapper(o)
                result_list.append(converted_o)
        except:
            print("객체화 불가")
            result_list = list_obj
        return result_list

 

 

 

개발이슈 : 버퍼사이즈만큼 분명 데이터를 보내고 있는데, 무언가 더 보내지고 있다.(recv 했을 때 버퍼가 다 안차서 그런지 뭘 계속 더 받으려고 한다) -> 의미 없는 데이터의 경우 무시되도록 len으로 지워버림

 

def receive_message(self):
        while True:
            if self.is_keep_on_thread is False:
                break

            try:
                print("Client_listening")
                recv_encoded_message = self.client_socket.recv(self.common.BUFFER)
                temp = recv_encoded_message.decode(self.common.FORMAT).strip()
                if len(temp) < 3:
                    continue
                response_header, response_data = self.header_data_distributor(temp)
                print(f"CLIENT RECEIVED{type(response_data)}: ({response_header},{response_data})")
            except OSError:
                continue
            except Exception:
                traceback.print_exc()
                continue

 


 

오늘 늘린 기술 : Thread 내 Exception traceback 받기

 

def receive_message(self, client_socket: socket):
        while True:
            try:
                recv_encoded_message = client_socket.recv(self.common.BUFFER)
                if len(recv_encoded_message) < 3:
                    continue
                request_header, request_data = self.header_data_distributor(recv_encoded_message)
                print(f"SERVER RECEIVED: ({request_header},{request_data})")

            except Exception:
                traceback.print_exc()
                return False

            if request_header == self.common.LOGIN_ACCESS_REQ:
                username = request_data['login_username']
                password = request_data['password']
                login_employee = self.db_conn.assert_login_username_and_password(username, password)
                if login_employee is None:
                    self.send_message(client_socket, self.common.LOGIN_ACCESS_RES, self.common.FALSE)
                else:
                .....

 

try exception 구조에 traceback 을 받으면 해당 에러와 문제 부분을 리턴해주어 디버깅할 때 좋은 참고가 된다.


 

평가:

관계가 점점 복잡해지고, 전달해야하는 데이터들 가지고 오는 것도 쉽지 않아진다.

그래도 내일까지는 어느정도 채팅 구현까지는 될 것 같은 느낌이 든다. 모든 데이터들을 input 처리하진 못하겠지만

하는데까지 최선을 다해보려고 한다.

 

감사합니다.

 

 

금일 목표 : CRUD 로직 완성, 더미 데이터 입력하기, UI-클라이언트-네트워크 로직 시작

 

실제 달성 : CRUD 로직 일부 완성, 더미 데이터 완료, UI-클라이언트-네트워크 로직 진행중

 

평가:

비효율적으로 필요없는 CRUD 로직은 작성하지 않으려고 계획중이다. (필요할 때마다 그때 그때 생성중)

DTO 를 활용하여 기존 CRUD 로직을 최대한 활용하려고 하는데, 매번 Object로 맵핑한다는 것은 굉장한 TRIAL이다.

 

토요일 자정까지 마감기한이 늘어났다. 로직을 깔끔하게 짜서 시간을 아끼려고 함에도,

구현해야할 분량이 여태 한 프로젝트 중 가장 많은 것 같다...

 


 

# DBConnector.py
 # ================ CREATE TABLE ============================ #
    def create_tables(self):
        c = self.start_conn()
        pstmt = None
        with open(self.SQL_PATH, 'r', encoding='utf-8') as file:
            pstmt = file.read()
        if pstmt is None:
            raise 'sql문 못불러옴'
        c.executescript(pstmt)
        self.commit_db()
        self.end_conn()
        print('created_tables')

    # ===================== DOMAIN ============================ #
    # ================ EMPLOYEE ============================ #
    def insert_employee(self, employee: Employee):
        assert isinstance(employee, Employee)
        c = self.start_conn()
        if employee.employee_id is None:
            # insert
            pstmt, values, = self.make_prepared_statement(employee, 'tb_employee')
            c.execute(pstmt, values)
        else:
            # todo: update 로직 필요하면 작성
            return
        self.commit_db()
        row = c.execute('select * from tb_employee order by employee_id desc limit 1').fetchone()
        result = Employee(*row)
        self.end_conn()
        return result

    def find_employee_by_id(self, employee_id):
        fetched_row = self.find_obj_by_id_and_table_name(employee_id, "tb_employee", "employee")
        return Employee(*fetched_row)

    def find_all_employee(self):
        fetched_rows = self.find_all_by_table_name("tb_employee")
        result_list = list()
        for row in fetched_rows:
            result_list.append(Employee(*row))
        return result_list

    def assert_login_username_and_password(self, username, password):
        c = self.start_conn()
        fetched_row = c.execute(f'select * from tb_employee where login_username=? and login_password=?',
                                (f'{username}', f'{password}',)).fetchone()

        if fetched_row is None:
            return None
        return Employee(*fetched_row)
    # ================ DOCTOR / NURSE / ADMIN ============================ #
    def insert_doctor_job(self, employee, assigned_department_id):
        c = self.start_conn()
        c.execute(f'''insert into tb_doctor(employee_id, assigned_department_id)
            values (?, ?)''', (employee.employee_id, assigned_department_id,))
        self.commit_db()
        self.end_conn()

    def insert_nurse_job(self, employee, assigned_ward_id):
        c = self.start_conn()
        c.execute(f'''insert into tb_nurse(employee_id, assigned_ward_id)
                    values (?, ?)''', (employee.employee_id, assigned_ward_id,))
        self.commit_db()
        self.end_conn()

    def insert_admin_job(self, employee, assigned_part_id):
        c = self.start_conn()
        c.execute(f'''insert into tb_administration(employee_id, assigned_part_id)
                    values (?, ?)''', (employee.employee_id, assigned_part_id,))
        self.commit_db()
        self.end_conn()

    # ================ CHAT ROOM ============================ #
    def insert_chat_room(self, chat_room: ChatRoom):
        assert isinstance(chat_room, ChatRoom)
        c = self.start_conn()
        if chat_room.chat_room_id is None:
            # insert
            pstmt, values, = self.make_prepared_statement(chat_room, 'tb_chat_room')
            c.execute(pstmt, values)
        else:
            # todo: update 로직 필요하면 작성
            pass
        self.commit_db()
        row = c.execute('select * from tb_chat_room order by chat_room_id desc limit 1').fetchone()
        result = ChatRoom(*row)
        self.end_conn()
        return result

    def find_chat_room_by_id(self, chat_room_id):
        assert isinstance(chat_room_id, int)
        fetched_row = self.find_obj_by_id_and_table_name(chat_room_id, "tb_chat_room", "chat_room")
        # 관련 톡방 사용자(2명) list 가져오기
        employee_list = self.find_employees_by_chat_room_id(chat_room_id)
        # message_list 가져오기
        message_list = self.find_messages_by_chat_room_id(chat_room_id)

        return ChatRoom(*fetched_row, employee_list=employee_list, message_list=message_list)

    def find_employees_by_chat_room_id(self, chat_room_id):
        c = self.start_conn()
        fetched_rows = c.execute(f"""
            select * from tb_employee where employee_id in 
            (select employee_id from tb_employee_chat_room where chat_room_id = {chat_room_id})
        """).fetchall()
        result_list = list()
        for row in fetched_rows:
            result_list.append(Employee(*row))
        self.end_conn()
        return result_list
    
    # ================ EMPLOYEE CHAT ROOM ============================ #
    def insert_employee_chat_room(self, employee_id, chat_room_id):
        assert isinstance(employee_id, int) and isinstance(chat_room_id, int)
        c = self.start_conn()
        c.execute('insert into tb_employee_chat_room(employee_id, chat_room_id) values (?, ?)',
                  (employee_id, chat_room_id))
        self.commit_db()
        self.end_conn()

    # ================ MESSAGE ============================ #
    def insert_message(self, message: Message):
        assert isinstance(message, Message)
        c = self.start_conn()
        if message.message_id is None:
            # insert
            pstmt, values, = self.make_prepared_statement(message, 'tb_message')
            c.execute(pstmt, values)
        else:
            # todo: update 로직 필요하면 작성
            pass
        self.commit_db()
        row = c.execute('select * from tb_message order by message_id desc limit 1').fetchone()
        result = Message(*row)
        self.end_conn()
        return result

    def find_messages_by_message_id(self, message_id):
        c = self.start_conn()
        fetched_row = c.execute(f"""
            select * from tb_message where message_id = {message_id})
        """).fetchone()
        result = Message(*fetched_row)
        self.end_conn()
        return result

    def find_messages_by_chat_room_id(self, chat_room_id):
        c = self.start_conn()
        fetched_rows = c.execute(f"""
            select * from tb_message where message_id in 
            (select employee_id from tb_employee_chat_room where chat_room_id = {chat_room_id})
        """).fetchall()
        result_list = list()
        for row in fetched_rows:
            result_list.append(Message(*row))
        self.end_conn()
        return result_list

    def checked_message_by_chat_room_id(self, chat_room_id):
        c = self.start_conn()
        fetched_rows = c.execute(f"""
            update tb_message set "is_confirmed"=1 where message_id in 
            (select employee_id from tb_employee_chat_room where chat_room_id = {chat_room_id})
        """).fetchall()
        result_list = list()
        for row in fetched_rows:
            result_list.append(Message(*row))
        self.end_conn()
        return result_list

아직 로그인 기능만 구현한 서버

class EMRServer:
    def __init__(self, db_connector: DBConnector):
        self.db_conn = db_connector
        self.common = Common()

        self.server_socket = None
        self.sockets_list = list()
        self.clients = dict()
        self.thread_for_run = None
        self.run_signal = True

        self.decoder = ObjDecoder()
        self.encoder = ObjEncoder()

        self.dto_maker = DTOMaker()

    def start(self):
        if self.thread_for_run is not None:  # 실행중이면 종료 시키기
            return
        self.server_socket = socket(AF_INET, SOCK_STREAM)  # AF_INET(ipv4를 의미)
        self.server_socket.bind((self.common.HOST, self.common.PORT))  # 바인딩
        self.server_socket.listen()  # 리슨 시작
        self.sockets_list.clear()  # 소켓리스트 클리어
        self.sockets_list.append(self.server_socket)
        self.run_signal = True
        self.thread_for_run = Thread(target=self.run)
        self.thread_for_run.start()
        print('SERVER STARTED')

    def run(self):
        while True:
            if self.run_signal is False:
                break
            try:
                read_sockets, _, exception_sockets = select.select(self.sockets_list, [], self.sockets_list, 0.1)
            except Exception:
                continue
            for notified_socket in read_sockets:
                if notified_socket == self.server_socket:
                    client_socket, client_address = self.server_socket.accept()
                    user = self.receive_message(client_socket)
                    if user is False:
                        continue
                    self.sockets_list.append(client_socket)
                    self.clients[client_socket] = user

                else:
                    message = self.receive_message(notified_socket)

                    if message is False:
                        self.sockets_list.remove(notified_socket)
                        del self.clients[notified_socket]
                        continue

            for notified_socket in exception_sockets:
                self.sockets_list.remove(notified_socket)
                del self.clients[notified_socket]

    def send_message(self, client_socket:socket, header, data):
        assert isinstance(header, str) or isinstance(header, bytes)
        assert isinstance(data, str) or isinstance(data, bytes)
        if isinstance(header, bytes):
            header_to_sending = header.decode(self.common.FORMAT)
        else:
            header_to_sending = header
        if isinstance(data, bytes):
            data_to_sending = data.decode(self.common.FORMAT)
        else:
            data_to_sending = data

        message_to_send = f"{self.common.START_OF_TEXT}".join([header_to_sending, data_to_sending]) \
            .encode(self.common.FORMAT)

        if len(message_to_send) >= self.common.BUFFER:
            raise "부족한 버퍼 용량"
        print(f"SERVER SENDED: (HEADER: {header} | DATA: {data})")
        client_socket.send(message_to_send)

    def header_data_distributor(self, recv_encoded_message):
        decoded_message = recv_encoded_message.decode(self.common.FORMAT).strip()
        header, data_decoded_str, = decoded_message.split(self.common.START_OF_TEXT)
        data = self.decoder.binary_to_obj(data_decoded_str)
        return header, data

    def receive_message(self, client_socket: socket):
        while True:
            try:
                recv_encoded_message = client_socket.recv(self.common.BUFFER)
                request_header, request_data = self.header_data_distributor(recv_encoded_message)
                print(f"SERVER RECEIVED: ({request_header},{request_data})")
            except Exception:
                return False

            if request_header == self.common.LOGIN_ACCESS_REQ:
                username = request_data['login_username']
                password = request_data['password']
                login_employee = self.db_conn.assert_login_username_and_password(username, password)
                if login_employee is None:
                    self.send_message(client_socket, self.common.LOGIN_ACCESS_RES,self.common.FALSE)
                else:
                    # 로그인 성공 로직
                    # 회원 정보 보내기
                    data_str = self.encoder.toJSON_an_object(login_employee)
                    self.send_message(client_socket, self.common.LOGIN_ACCESS_RES, data_str)

오늘 작성한 SQL, 일부 더미데이터를 추가했다 내일 몇가지 관계를 추가해야해서 머리가 아프다...;

DROP TABLE IF EXISTS tb_ktas;
CREATE TABLE "tb_ktas" (
	"ktas_id"	INTEGER,
	"first_category_name"	TEXT NOT NULL,
	"first_category_code"	TEXT NOT NULL,
	"second_category_name"	TEXT NOT NULL,
	"second_category_code"	TEXT NOT NULL,
	"third_category_name"	TEXT NOT NULL,
	"third_category_code"	TEXT NOT NULL,
	"fourth_category_name"	TEXT NOT NULL,
	"fourth_category_code"	TEXT NOT NULL,
	"final_grade"	TEXT NOT NULL,
	"ktas_code"	TEXT NOT NULL,
	PRIMARY KEY("ktas_id" AUTOINCREMENT)
);

DROP TABLE IF EXISTS tb_employee;
CREATE TABLE "tb_employee" (
	"employee_id"	INTEGER,
	"name"	TEXT NOT NULL,
	"type_job"	INTEGER NOT NULL,
	"login_username"	TEXT NOT NULL UNIQUE,
	"login_password"	TEXT NOT NULL,
	"mobile_phone_num_1"	TEXT,
	"mobile_phone_num_2"	TEXT,
	PRIMARY KEY("employee_id" AUTOINCREMENT)
);

DROP TABLE IF EXISTS tb_department;
CREATE TABLE "tb_department" (
    "department_id"	INTEGER,
    "name"	TEXT NOT NULL,
    "job_category"	INTEGER NOT NULL,
    PRIMARY KEY("department_id" AUTOINCREMENT)
);
DROP TABLE IF EXISTS tb_doctor;
CREATE TABLE "tb_doctor" (
	"doctor_id"	INTEGER,
	"employee_id"	INTEGER NOT NULL,
	"assigned_department_id"	INTEGER NOT NULL,
	PRIMARY KEY("doctor_id" AUTOINCREMENT)
	FOREIGN KEY("assigned_department_id") REFERENCES "tb_department"("department_id")
);

DROP TABLE IF EXISTS tb_nurse;
CREATE TABLE "tb_nurse" (
	"nurse_id"	INTEGER,
	"employee_id"	INTEGER NOT NULL,
	"assigned_ward_id"	INTEGER NOT NULL,
	PRIMARY KEY("nurse_id" AUTOINCREMENT),
	FOREIGN KEY("employee_id") REFERENCES "tb_employee"("employee_id")
	FOREIGN KEY("assigned_ward_id") REFERENCES "tb_department"("department_id")
);

DROP TABLE IF EXISTS tb_administration;
CREATE TABLE "tb_administration" (
    "administration_id"	INTEGER,
    "employee_id"	INTEGER NOT NULL,
    "assigned_part_id"	INTEGER NOT NULL,
    PRIMARY KEY("administration_id" AUTOINCREMENT),
    FOREIGN KEY("employee_id") REFERENCES "tb_employee"("employee_id"),
    FOREIGN KEY("assigned_part_id") REFERENCES "tb_department"("department_id")
);
DROP TABLE IF EXISTS tb_chat_room;
CREATE TABLE "tb_chat_room" (
    "chat_room_id"	INTEGER,
    "created_time"	TEXT NOT NULL,
    PRIMARY KEY("chat_room_id" AUTOINCREMENT)
);
DROP TABLE IF EXISTS tb_employee_chat_room;
CREATE TABLE "tb_employee_chat_room" (
	"employee_chat_room_id"	INTEGER,
	"chat_room_id"	INTEGER NOT NULL,
	"employee_id"	INTEGER NOT NULL,
	FOREIGN KEY("chat_room_id") REFERENCES "tb_chat_room"("chat_room_id"),
	FOREIGN KEY("employee_id") REFERENCES "tb_employee"("employee_id"),
	PRIMARY KEY("employee_chat_room_id" AUTOINCREMENT)
);

DROP TABLE IF EXISTS tb_patient;
CREATE TABLE "tb_patient" (
	"patient_id"	INTEGER,
	"birth_date"	TEXT,
	"name"	TEXT NOT NULL,
	"sex"	TEXT NOT NULL,
	"ssn"	TEXT UNIQUE,
	"address"	TEXT,
	"type_insurance"	TEXT,
	"allocated_bed_location_id"	INTEGER,
	"register_number"	INTEGER UNIQUE,
	FOREIGN KEY("allocated_bed_location_id") REFERENCES "tb_bed_of_ward"("bed_id"),
	PRIMARY KEY("patient_id" AUTOINCREMENT)
);

DROP TABLE IF EXISTS tb_bed_of_ward;
CREATE TABLE "tb_bed_of_ward" (
    "bed_id"	INTEGER,
    "assigned_ward_id"	INTEGER NOT NULL,
    "additional_info"	TEXT,
    "viewing_bed_name"	TEXT NOT NULL,
    PRIMARY KEY("bed_id" AUTOINCREMENT),
    FOREIGN KEY("assigned_ward_id") REFERENCES "tb_department"
);

DROP TABLE IF EXISTS tb_emergency_nurse_record;
CREATE TABLE "tb_emergency_nurse_record" (
	"enr_id"	INTEGER,
	"register_id"	INTEGER,
	"onset_time"	TEXT,
	"ktas_id"	INTEGER,
	"cheif_complain"	TEXT,
	"description"	TEXT,
	"recorder_nurse_id"	INTEGER,
	"saved_time"	TEXT,
	"responser"	TEXT,
	PRIMARY KEY("enr_id" AUTOINCREMENT),
	FOREIGN KEY("recorder_nurse_id") REFERENCES "tb_nurse"("nurse_id")
	FOREIGN KEY("register_id") REFERENCES "tb_patient"("register_number")
);



DROP TABLE IF EXISTS tb_message;
CREATE TABLE "tb_message" (
	"message_id"	INTEGER,
	"sender_employee_id"	INTEGER NOT NULL,
	"chat_room_id"	INTEGER NOT NULL,
	"contents"	TEXT NOT NULL,
	"is_confirmed"	INTEGER NOT NULL,
	FOREIGN KEY("chat_room_id") REFERENCES "tb_chat_room"("chat_room_id"),
	PRIMARY KEY("message_id" AUTOINCREMENT),
	FOREIGN KEY("sender_employee_id") REFERENCES "tb_employee"("employee_id")
);

INSERT INTO tb_department("name", "job_category") VALUES
("응급의학과", 1),("신경외과", 1), ("흉부외과", 1), ("외과", 1), ("내과", 1), ("소아청소년과", 1),
("7A",2),("9B",2),("ER",2),("MICU",2),("101W",2),("EICU",2),
("원무과",3),("보험심사팀",3);

INSERT INTO tb_bed_of_ward("assigned_ward_id", "additional_info", "viewing_bed_name") VALUES
(9, "소생실", "CR1"),
(9, "소생실", "CR2"),
(9, "중증구역", "IC1"),
(9, "중증구역", "IC2"),
(9, "중증구역", "IC3"),
(9, "중증구역", "IC4"),
(9, "중증구역", "IC5"),
(9, "중증구역", "IC6"),
(9, "중증구역", "IC7"),
(9, "중증구역", "IC8"),
(9, "중증구역", "IC9"),
(9, "중증구역", "IC10"),
(9, "중증구역", "IC11"),
(9, "중증구역", "IC12"),
(9, "경증구역", "TR");


INSERT INTO tb_patient("birth_date", "name","sex", "ssn", "address", "type_insurance", "allocated_bed_location_id", "register_number") VALUES
('1958-06-08', '김철수', 'M', '580608-1354644', '경기도 수원시 언저리 어딘가', '건강보험', 1, 486124);

INSERT INTO tb_emergency_nurse_record(
    "onset_time",
    "register_id",
    "ktas_id",
    "cheif_complain",
    "description",
    "recorder_nurse_id",
    "saved_time",
    "responser") values
('1958-06-08',486124, 'AICAB', '가슴이 답답해요',
"내원전 2시간 전, 속 울렁거림, 가슴 답답함 느껴졌으나 시간 지나니 괜찮아짐, 식사 후 집으로 귀가하던 중 가슴 부여잡은 채 쓰러진 것 행인이 목격, 119 신고로 이송됨 이송 중에 의식 깨어 현재는 alert하나 흉통(NRS>7) 계속 호소중임",
1, "2023-07-20 13:11:22", "환자 본인");

더미 데이터 추가 로직

def insert_dummy_employee_data(conn: DBConnector, size):
    # 우선 내 거 하나
    conn.insert_employee(Employee(None, "박광현", 2, "qwer11", "1234", "010-1010-0102", "010-0215-1335"))
    maker = FakeDataMaker()
    for i in range(size):
        # 가짜 생성
        name = maker.get_random_fake_name()
        type_job = maker.get_random_type_job()
        login_username = maker.get_random_login_id()
        login_pw = "1234"
        phone_1 = maker.get_random_phone_num()
        phone_2 = maker.get_random_phone_num()
        # 객체화 및 입력
        dummy_employee = Employee(None, name, type_job, login_username, login_pw, phone_1, phone_2)
        dummy_employee = conn.insert_employee(dummy_employee)
        if dummy_employee.type_job == 1:
            conn.insert_doctor_job(dummy_employee, random.randint(1, 6))
        elif dummy_employee.type_job == 2:
            conn.insert_nurse_job(dummy_employee, random.randint(7, 12))
        elif dummy_employee.type_job == 3:
            conn.insert_admin_job(dummy_employee, random.randint(13, 14))
def insert_dummy_chat_room(conn: DBConnector, size):
    maker = FakeDataMaker()
    for i in range(size):
        # 가짜 생성
        # 객체화 및 입력
        random_date = maker.get_random_date_time_str()
        dummy_chat_room = ChatRoom(None, random_date)
        conn.insert_chat_room(dummy_chat_room)


def insert_dummy_employee_chat_room_data(conn: DBConnector, size, employee_range, chat_range):
    maker = FakeDataMaker()
    min_id_employee, max_id_employee = employee_range
    min_id_chat, max_id_chat = chat_range
    for i in range(size):
        # 가짜 생성
        # 객체화 및 입력
        random_employee_id_1, random_employee_id_2 = random.sample(range(min_id_employee, max_id_employee), 2)
        random_chat_room_id = random.randint(min_id_chat, max_id_chat)
        conn.insert_employee_chat_room(random_employee_id_1, random_chat_room_id)
        conn.insert_employee_chat_room(random_employee_id_2, random_chat_room_id)
        for _ in range(random.randint(1, 10)):
            # 가짜 생성
            # 객체화 및 입력
            dummy_message_1 = Message(None, random_employee_id_1, random_chat_room_id, maker.get_random_lorem(), False)
            dummy_message_2 = Message(None, random_employee_id_2, random_chat_room_id, maker.get_random_lorem(), False)
            conn.insert_message(dummy_message_1)
            conn.insert_message(dummy_message_2)


def insert_dummy_message_data(conn: DBConnector, size, employee_range, chat_range):
    maker = FakeDataMaker()
    min_id_employee, max_id_employee = employee_range
    min_id_chat, max_id_chat = chat_range
    for i in range(size):
        # 가짜 생성
        # 객체화 및 입력
        random_employee_id = random.randint(min_id_employee, max_id_employee)
        random_chat_room_id = random.randint(min_id_chat, max_id_chat)
        dummy_message = Message(None, random_employee_id, random_chat_room_id, maker.get_random_lorem(), False)
        conn.insert_message(dummy_message)

 

내일은 UI 구현, 실시간 채팅을 구현할 예정이다.

 

감사합니다.

 

 

오늘 목표 : DB 작성, ORM 로직 구현

실제 한일 : DB 작성, 더미 데이터 작성 일부, ORM 로직 일부 구현

 

평가:

테이블과 Domain class 개체들이 많아졌다.

쿼리 입력을 key-value 로 하는 방법이 있을까 했는데, 역시 있었다.

kwargs 전달만으로 database에 query를 입력하는 방법을 습득 및 구현하는 시간을 가졌다.

또한, ktas(응급환자 중증도 분류) row 가 4366 줄이었다. 데이터를 전처리하느라 시간을 조금 썼지만,

3-4시간 정도만에 DB입력까지 완료했으니, 길긴 했지만 이전에 비해 성장했다고 느낀다.

 


import json

from Domain.message import Message


class ObjEncoder(json.JSONEncoder):
    _instance = None

    def __new__(cls):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self):
        super().__init__()

    def toJSON_as_binary(self, obj):
        if isinstance(obj, list):
            temp_list = list()
            for o in obj:
                str_obj = self.toJSON_an_object(o)
                temp_list.append(str_obj)
            list_json = json.dumps(temp_list, default=lambda o: o.__dict__)
            return list_json.encode('utf-8')
        return self.toJSON_an_object_with_encode(obj)

    def toJSON_an_object_with_encode(self, obj):
        json_string = self.toJSON_an_object(obj)
        return json_string.encode('utf-8')

    def toJSON_an_object(self, obj):
        print(obj.__dict__)
        string_converted_obj = json.dumps(obj, default=lambda o: o.__dict__)
        json_string = self.encode(string_converted_obj)
        return json_string


class ObjDecoder(json.JSONDecoder):
    _instance = None

    def __new__(cls):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self):
        super().__init__()

    def binary_to_obj(self, binary_str):
        if isinstance(binary_str, bytes): # binary -> utf-8
            binary_str = binary_str.decode('utf-8')
        json_string = json.loads(binary_str)  # utf-8 -> json
        if isinstance(json_string, list):
            result_obj = self.list_mapper(json_string)
        else:
            result_obj = self.object_mapper(json_string)
        if result_obj is not None:
            return result_obj
        return json_string

        # json_to_object = json.loads(json_string)  # json -> dict(default)
    def object_mapper(self, dict_obj):
        if isinstance(dict_obj, str):
            dict_obj = json.loads(dict_obj)
            dict_obj = json.loads(dict_obj)
        assert isinstance(dict_obj, dict)
        if "is_confirmed" in dict_obj.keys():
            return Message(**dict_obj)

    def list_mapper(self, list_obj):
        assert isinstance(list_obj, list)
        result_list = list()
        for o in list_obj:
            converted_o = self.object_mapper(o)
            result_list.append(converted_o)
        return result_list


if __name__ == '__main__':
    msg_1 = Message(1, 1, 22, "안녕", False)
    msg_2 = Message(2, 1, 21, "sdfsdlfsjdfl", False)
    msg_3 = Message(None, 7, 21, "abcde", False)
    msg_4 = Message(2, 5, 100, "fghij", True)

    encoder = ObjEncoder()
    decoder = ObjDecoder()

    list_obj_ = [msg_1, msg_2, msg_3, msg_4]
    bytes_list_obj = encoder.toJSON_as_binary(list_obj_)

    result_list = decoder.binary_to_obj(bytes_list_obj)

 

오늘 객체 kwargs로 insert 집어넣기

?(물음표) 를 사용하지 않아서 도메인 attribute 의 순서와 개수에 영향을 덜 타게 되었다.

 

# ================ EMPLOYEE ============================ #
    def insert_employee(self, employee: Employee):
        assert isinstance(employee, Employee)
        c = self.start_conn()
        if employee.employee_id is None:
            # insert
            keys = ','.join(employee.__dict__.keys())
            colon_keys = ','.join([f':{x}' for x in employee.__dict__.keys()])
            pstmt = f'insert into tb_employee ({keys}) values ({colon_keys})'
            values = employee.__dict__
            c.execute(pstmt, values)
        else:
            # todo: update 로직 필요하면 작성
            pass

        self.commit_db()
        row = c.execute('select * from tb_employee order by employee_id asc limit 1').fetchone()
        result = Employee(*row)
        self.end_conn()
        return result
    # 이전 스타일
	def insert_user(self, user_object: User):
        c = self.start_conn()
        user_id = user_object.user_id
        user_name = user_object.username
        password = user_object.password
        nickname = user_object.nickname
        if users_id is None:
            c.execute('insert into user(username, password, nickname) values (?, ?, ?)',
                      (user_name, password, nickname))
            self.commit_db()
            inserted_user_row = c.execute('select * from user order by user_id desc limit 1').fetchone()
            inserted_user_obj = User(*inserted_user_row)
            self.end_conn()
            return inserted_user_obj
        else:
            updated_user_obj = self.update_user(user_object)
            return updated_user_obj

 

응급실 중증도 환자 분류는 여기의 표를 참고하였습니다.

http://www.mohw.go.kr/react/jb/sjb0406vw.jsp?PAR_MENU_ID=03&MENU_ID=030406&CONT_SEQ=329273&page=1

 

정보 > 법령 > 훈령/예규/고시/지침 내용보기 " 「한국 응급환자 중증도 분류기준」고시 제정 " |

훈령/예규/고시/지침 「한국 응급환자 중증도 분류기준」고시 제정 등록일 : 2015-12-29 [최종수정일 : 2015-12-30] 조회수 : 15971 담당자 : 김미정( ☎ 044-202-2553 ) 담당부서 : 응급의료과 제·개정 구분 :

www.mohw.go.kr

 

hwp 파일이라 뷰어나 pdf로는 데이터를 한번에 불러오는게 쉽지 않았지만, split, regex(정규식) 등을 통해 결국 데이터를 정리했습니다.

def insert_KTAS_data(conn):
    second_category_name_set = set()
    third_category_name_set = set()
    fourth_category_name_set = set()

    whole_data = None

    with open("../data/emergency_grade.txt", 'r', encoding='utf-8') as file:
        whole_data = file.read()

    if whole_data is None:
        raise '파일 못읽음'

    lines = whole_data.split('\n')

    result_list = list()
    for row in lines:
        word_list = row.split(' ')
        temp_list = list()

        slice_index = -1
        for i, w in enumerate(word_list):
            if len(w) == 1:
                slice_index = i
                break
        for w in word_list[:slice_index]:
            temp_list.append(word_list.pop(0))

        if slice_index == -1:
            raise '못자르고 있음'

        second_category_name = ','.join(temp_list)
        second_category_name_set.add(second_category_name)  ## 세트 추가
        second_category_code = word_list.pop(0)
        temp_list.clear()

        while not re.match(r"[A-Z]", word_list[0]):
            temp_list.append(word_list.pop(0))
        third_category_name = ' '.join(temp_list)
        third_category_name_set.add(third_category_name)  ## 세트 추가
        third_category_code = word_list.pop(0)
        temp_list.clear()

        while not re.match(r"[A-Z]{2}", word_list[0]):
            temp_list.append(word_list.pop(0))

        fourth_category_name = ' '.join(temp_list)
        fourth_category_name_set.add(fourth_category_name)  ## 세트 추가
        fourth_category_code = word_list.pop(0)
        temp_list.clear()

        final_grade = int(word_list.pop())
        result_list.append(
            KTAS('성인', 'A', second_category_name, second_category_code, third_category_name, third_category_code,
                 fourth_category_name, fourth_category_code, final_grade))

    with open("../data/child_grade.txt", 'r', encoding='utf-8') as file:
        whole_data = file.read()

    if whole_data is None:
        raise '파일 못읽음'

    lines = whole_data.split('\n')

    for row in lines:
        word_list = row.split(' ')
        temp_list = list()

        slice_index = -1
        for i, w in enumerate(word_list):
            if len(w) == 1:
                slice_index = i
                break
        for w in word_list[:slice_index]:
            temp_list.append(word_list.pop(0))

        if slice_index == -1:
            raise '못자르고 있음'

        second_category_name = ','.join(temp_list)
        second_category_name_set.add(second_category_name)  ## 세트 추가
        second_category_code = word_list.pop(0)
        temp_list.clear()

        while not re.match(r"[A-Z]", word_list[0]):
            temp_list.append(word_list.pop(0))
        third_category_name = ' '.join(temp_list)
        third_category_name_set.add(third_category_name)  ## 세트 추가
        third_category_code = word_list.pop(0)
        temp_list.clear()

        while not re.match(r"[A-Z]{2}", word_list[0]):
            temp_list.append(word_list.pop(0))

        fourth_category_name = ' '.join(temp_list)
        fourth_category_name_set.add(fourth_category_name)  ## 세트 추가
        fourth_category_code = word_list.pop(0)
        temp_list.clear()

        final_grade = int(word_list.pop())

        result_list.append(
            KTAS('소아', 'B', second_category_name, second_category_code, third_category_name, third_category_code,
                 fourth_category_name, fourth_category_code, final_grade))

    c= conn.start_conn()
   
    for i in result_list:
        c.execute('''INSERT INTO tb_ktas (
         first_category_name, first_category_code, second_category_name,
         second_category_code, third_category_name, third_category_code,
         fourth_category_name, fourth_category_code, final_grade)
         VALUES (
         :first_category_name, 
         :first_category_code, 
         :second_category_name,
         :second_category_code,
         :third_category_name, 
         :third_category_code, 
         :fourth_category_name,
         :fourth_category_code,
         :final_grade);''', i.__dict__)
    conn.commit_db()
    conn.end_conn()

 

진행도는 아직 전체분량의 30~40 % 정도까지만 진행되었고, 데이터 손댄 김에 더미 데이터를 넣어놔야 테스트하기 편할 것 같아

내일 오전까지는 더미데이터 입력, Domain 완성, CRUD ORM, DTO 작성을 해놓고, 서버-클라이언트-UI 작업이 이어질 것 같습니다.

 

감사합니다.

 

 

금일 목표량

UI 작성, DB 테이블 작성 예정

 

실제 작업량

UI 작성, 파일 설계, 네트워크 소스 일부 구현, ERD 관계 맵핑

 

계획과 달라진 이유

-> 네트워크 부분에서 팀원들 도움이 필요한 부분 많음, 또한 시그널 구조를 어떻게 할지 아직 고민중이라 먼저 시작하였음

 

어제 작성한 ERD는 미완성 상태였다.

관계를 더 작성하였다.

 


오늘 뽑은 UI

 

 
 

 

오늘 뽑은 파일구조

아직은 거의 대부분 빈파일들이다

 


import datetime
import time

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import QPoint, Qt, pyqtSignal

from Client.class_widget_admin import WidgetAdmin
from Client.class_widget_chat_room import WidgetChatRoom
from Client.class_widget_dashboard import WidgetDashBoard
from Client.class_widget_employee_finder import WidgetEmployeeFinder
from Client.class_widget_login import WidgetLogin
from Client.class_widget_medical import WidgetMedical
from Client.client_connector import Connector


class WidgetController(QtWidgets.QWidget):
    def __init__(self, connector=Connector):
        assert isinstance(connector, Connector)
        super().__init__()
        self.connector = connector  # db연결 인스턴스
        self.connector.set_widget(self)

        self.widget_admin = None
        self.widget_chat_room = None
        self.widget_dashboard = None
        self.widget_e_finder = None
        self.widget_login = None
        self.widget_medical = None

        self.setUp()

    def run(self):
        self.show()

    def reset(self):
        pass

    def setUp(self):
        self.set_up_widgets()

    def close(self):
        pass

    def show(self):
        self.widget_admin.show()
        self.widget_chat_room.show()
        self.widget_dashboard.show()
        self.widget_e_finder.show()
        self.widget_login.show()
        self.widget_medical.show()

    def set_up_widgets(self):
        self.widget_admin = WidgetAdmin(self)
        self.widget_chat_room = WidgetChatRoom(self)
        self.widget_dashboard = WidgetDashBoard(self)
        self.widget_e_finder = WidgetEmployeeFinder(self)
        self.widget_login = WidgetLogin(self)
        self.widget_medical = WidgetMedical(self)

    @staticmethod
    def clear_widget(widget):
        if widget.layout() is not None:
            while widget.layout().count() > 0:
                item = widget.layout().takeAt(0)
                if item.widget():
                    item.widget().deleteLater()

 

UI 컨트롤러를 구성하여 인스턴스화한 위젯을 계속 빨아다 쓸 예정이다

(인스턴스를 많이 만들면 느려진다 - 키오스크때 경험)

 

이번 프로젝트도 패키지를 사용하다보니, 상대경로를 통해 이미지를 못불러오는 상황이 있었다.

다행히 qyc 모듈을 통해 인코딩하여 상대적으로 짧은 시간에 이미지들을 입혀서 인스턴스화된 객체로 화면들을 불러올 수 있었다.

 


추가로 관계까지 작성한 ERD

조금만 건들여도, 테이블이 확확 늘어나버린다. 내가 일을 그만두기 전에 차세대 EMR이라고 해서, 전산 시스템을 대대적으로 업그레이드하면서 몇천억을 썼었다고 했는데, 그 많은 요구사항들을 다 반영하려면.. 슈킹이 아니라 충분히 그럴만하다고 느껴진다.

 


 

오늘 들어온 질문들 (기억나는대로)

 

* JSON, Encoder, Decoder 어떻게 쓰는가? -> JSON 이 무엇인지 공부해라, dump 와 dumps의 차이 (s는 복수의 의미가 아니라 string임)

* Qt close() method overide -> controller 로 기능 이전해서 쓰기

* controller 로 어떻게 위젯 화면들을 제어하는가? -> 소스 코드 같이 분석하기

* 어떻게 총체적 합쳐진 클래스를 나눌 수 있는가? -> SOLID 소개 및 원칙을 준수해보기

* Message - Talk Room - User 테이블 간 적절한 칼럼의 관계도는? -> 원하는대로 결과 조회가 가능한지 계속 그려보기

 


 

감사합니다.

 

금일 과제 하달 및 분석, 프로젝트 계획 수립, 팀원 스터디, 외부강사 강의 수강을 진행하였습니다.


 

팀원 스터디 내용

참여 인원 : 10명 (공통되는 소재라 자유롭게 참석 가능했음)

전달 내용:

-> 왜? ERD? 관계형 DB 발생 원인

-> DB 기반 위의 프로그램 형식

-> 객체지향도 관계에 의해 동작

-> ERD 직접 그려보기

-> 1대 1, 1대 다, 다대 다 관계 설명

-> 유저 - 채팅방 - 채팅 / 상품 - 주문 - 유저 예시

 


 

강의 주제 : 인공지능이란 무엇일까? 광주 산업 클러스터 신설된 인공지능 업체 소개

초청 강사 : AICA(인공지능산업융합사업단) 김건오 책임님

요약 :

- 인공지능에 관련한 업체들이 타 지역에 비해 많이 입점하고 있음.

- 산업 단지들 내에 많은 스타트업들이 국가 예산을 지원받아 다양한 분야에서 활동 중

- 비전공자들도 충분히 활약할 수 있음

- 일자리가 열려있으니 많은 지원 부탁함

 


개발 계획서

 


 

개발일지

 

금일 프로젝트 진행한 것이 프로그램 구조 구상 및 ERD 작성 외 소스코드를 작성한 것은 없었습니다.

 

대신에 스터디 진행과 강연을 열심히 잘 들었습니다.

 

테이블을 계속 짜다보니 익숙해지는 것 같습니다. 분량이 많지만 늘 그렇든 최선을 다해보겠습니다.

 

감사합니다.

 

 

 

 

 

 



시연 영상
 

 

기능 영상 ) 회원 가입을 지원함

 

 

 

기능 영상 ) 침여중인 채팅방 인원을 보여줌

 

 
기능 영상) 메시지 저장 기능 (서버로 전송되어 저장됨)

이하는 소스 코드입니다.
 
감사합니다.
 

 

# Code/domain/class_db_connect
import sqlite3

from Code.domain.class_user import User
from Code.domain.class_user_talk_room import UserTalkRoom
from Code.domain.class_talk_room import TalkRoom
from Code.domain.class_message import Message
from Code.domain.class_long_contents import LongContents


class DBConnector:
    _instance = None

    def __new__(cls, test_option=None):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self, test_option=None):
        self.conn = None
        self.test_option = test_option

    def start_conn(self):
        if self.test_option is True:
            self.conn = sqlite3.connect('db_test.db')
        else:
            self.conn = sqlite3.connect('main_db.db')
        return self.conn.cursor()

    def end_conn(self):
        if self.conn is not None:
            self.conn.close()
            self.conn = None

    def commit_db(self):
        if self.conn is not None:
            self.conn.commit()
        else:
            raise f"cannot commit database! {self.__name__}"

    # CREATE TABLES =======================================================================
    def create_tables(self):
        c = self.start_conn()
        c.executescript("""
            DROP TABLE IF EXISTS user;
            CREATE TABLE "user" (
                "user_id"	INTEGER,
                "username"	TEXT NOT NULL UNIQUE,  
                "password"	TEXT NOT NULL,
                "nickname"	TEXT NOT NULL,
                PRIMARY KEY("user_id" AUTOINCREMENT)
            );
            DROP TABLE IF EXISTS user_talk_room;
            CREATE TABLE "user_talk_room" (
                "user_talk_room_id"	INTEGER,
                "user_id" INTEGER,
                "talk_room_id"	INTEGER,
                PRIMARY KEY("user_talk_room_id" AUTOINCREMENT)
            );
            DROP TABLE IF EXISTS talk_room;
            CREATE TABLE "talk_room" (
                "talk_room_id"	INTEGER,
                "talk_room_name" TEXT NOT NULL,
                "open_time_stamp"	TEXT NOT NULL,
                PRIMARY KEY("talk_room_id" AUTOINCREMENT)
            );
            DROP TABLE IF EXISTS message;
            CREATE TABLE "message" (
                "message_id" INTEGER, 
                "sender_user_id" INTEGER, 
                "talk_room_id" INTEGER,
                "send_time_stamp" TEXT, 
                "contents" TEXT, 
                "long_contents_id" INTEGER,
                PRIMARY KEY("message_id" AUTOINCREMENT)
            );
            DROP TABLE IF EXISTS long_contents;
            CREATE TABLE "long_contents" (
                "long_contents_id" INTEGER, 
                "contents_type" INTEGER, 
                "long_text" TEXT, 
                "image" BLOB,
                PRIMARY KEY("long_contents_id" AUTOINCREMENT)
            );

        """)
        self.commit_db()
        self.end_conn()

    # user 테이블========================================================================================
    # 인자로 들어온 User객체의 user_id 이미 존재하는지 확인, 존재하는 경우 update 함수 실행하는 로직 구현
    def insert_user(self, user_object: User):
        c = self.start_conn()
        user_id = user_object.user_id
        user_name = user_object.username
        password = user_object.password
        nickname = user_object.nickname
        users_id = c.execute('select * from user where user_id = ?', (user_id,)).fetchone()

        if users_id is None:
            c.execute('insert into user(username, password, nickname) values (?, ?, ?)',
                      (user_name, password, nickname))
            self.commit_db()
            inserted_user_row = c.execute('select * from user order by user_id desc limit 1').fetchone()
            inserted_user_obj = User(*inserted_user_row)
            self.end_conn()
            return inserted_user_obj
        else:
            updated_user_obj = self.update_user(user_object)
            return updated_user_obj

    def update_user(self, user_object: User):
        c = self.start_conn()
        user_id = user_object.user_id
        user_name = user_object.username
        password = user_object.password
        nickname = user_object.nickname
        c.execute('update user set username=?, password = ?, nickname=? where user_id = ?',
                  (user_name, password, nickname, user_id))
        self.commit_db()
        updated_user_row = c.execute('select * from user where user_id = ?', (user_id,)).fetchone()
        updated_user_obj = User(*updated_user_row)
        self.end_conn()
        return updated_user_obj

    # User 찾기
    def find_all_user(self):
        c = self.start_conn()
        user_data = c.execute('select * from user').fetchall()
        if user_data is None:
            return None
        all_user_obj_list = list()
        for row_user in user_data:
            all_user_obj_list.append(User(*row_user))
        self.end_conn()
        return all_user_obj_list

	def find_user_by_username(self, username):
        c = self.start_conn()
        user_data = c.execute('select * from user where username = ?', (username,)).fetchone()
        if user_data is None:
            return None
        user_object = User(*user_data)
        self.end_conn()
        return user_object

    def find_user_by_user_id(self, user_id):
        c = self.start_conn()
        user_data = c.execute('select * from user where user_id = ?', (user_id,)).fetchone()
        if user_data is None:
            return None
        user_object = User(*user_data)
        self.end_conn()
        return user_object

    # user 삭제
    def delete_user_by_username(self, username: str):
        c = self.start_conn()
        deleted_user = c.execute('select * from user where username = ?', (username,)).fetchone()
        deleted_user_obj = User(*deleted_user)
        c.execute('delete from user where username = ?', (username,))
        self.commit_db()
        self.end_conn()
        return deleted_user_obj

    def delete_user_by_user_id(self, user_id):
        c = self.start_conn()
        deleted_user = c.execute('select * from user where user_id = ?', (user_id,)).fetchone()
        deleted_user_obj = User(*deleted_user)
        c.execute('delete from user where user_id = ?', (user_id,))
        self.commit_db()
        self.end_conn()
        return deleted_user_obj

    # id 기준으로 함수로 하나 더 만들기

    # user_talk_room================================================
    # 새로운 채팅방 생성 함수 호출 및 user_talk_room 테이블 정보 추가
    # def insert_user_talk_room(self, user_id, talk_room_name, open_time_stamp):
    #     talk_room_obj = self.create_talk_room(talk_room_name, open_time_stamp)
    #     c = self.start_conn()
    #     talk_room_id = talk_room_obj.talk_room_id
    #     c.execute('insert into user_talk_room (user_id, talk_room_id) values (?, ?)', (user_id, talk_room_id))
    #     self.commit_db()
    #     self.end_conn()

    def insert_user_talk_room(self, user_talk_room_obj: UserTalkRoom):
        c = self.start_conn()
        # user_talk_room_id = user_talk_room_obj.user_talk_room_id
        user_id = user_talk_room_obj.user_id
        talk_room_id = user_talk_room_obj.talk_room_id
        c.execute('insert into user_talk_room (user_id, talk_room_id) values (?, ?)', (user_id, talk_room_id))
        self.commit_db()
        inserted_user_talk_room_row = c.execute('select * from user_talk_room order by user_talk_room_id desc limit 1').fetchone()
        inserted_user_talk_room_obj = UserTalkRoom(*inserted_user_talk_room_row)
        self.end_conn()
        return inserted_user_talk_room_obj

    def find_all_user_talk_room(self):
        c = self.start_conn()
        all_user_talk_room_obj_list = list()
        user_talk_room_rows = c.execute('select * from user_talk_room').fetchall()
        for row in user_talk_room_rows:
            user_talk_room_obj = UserTalkRoom(*row)
            all_user_talk_room_obj_list.append(user_talk_room_obj)
        self.end_conn()
        return all_user_talk_room_obj_list

    # 유저가 속한 모든 채팅방(TalkRoom) 객체 리스트로 반환
    def find_user_talk_room_by_user_id(self, user_id: int) -> list[UserTalkRoom]:
        c = self.start_conn()
        users_talk_room_obj_list = list()
        user_talk_rooms = c.execute('select * from user_talk_room where user_id = ?', (user_id,)).fetchall()
        # talk_rooms = c.execute('select * from talk_room').fetchall()
        for user_talk_row in user_talk_rooms:
            user_talk_room_obj = UserTalkRoom(*user_talk_row)
            users_talk_room_obj_list.append(user_talk_room_obj)

        self.end_conn()
        return users_talk_room_obj_list


    def find_user_talk_room_by_username(self, username: str) -> list[UserTalkRoom]:
        c = self.start_conn()
        username_row = c.execute('select * from user where username = ?', (username,)).fetchone()
        user_id = username_row[0]
        self.end_conn()
        result = self.find_user_talk_room_by_user_id(user_id)
        return result

    # talk_room_id에 해당하는 톡방에 있는 유저 객체 반환
    def find_user_by_talk_room_id(self, talk_room_id: int) -> list[User]:
        c = self.start_conn()
        all_user = list()
        involved_user = c.execute('select * from user_talk_room where talk_room_id = ?', (talk_room_id,)).fetchall()
        if len(involved_user) == 0:
            return None
        for user in involved_user:
            user_id = user[1]
            user = c.execute('select * from user where user_id = ?', (user_id,)).fetchone()
            all_user.append(User(*user))
        self.end_conn()
        return all_user

    # 회원이 자신이 속한 모든 톡방을 나간 경우(탈퇴 등-이미 탈퇴 함수 있음), user_id / username에 따라 삭제(유저가 속한 모든 톡방 나감)
    def delete_user_talk_room_by_user_id(self, user_id):
        c = self.start_conn()
        c.execute('delete from user_talk_room where user_id = ?', (user_id,))
        self.commit_db()
        self.end_conn()
         def delete_user_talk_room_by_username_and_talk_room_id(self, user_name, talk_room_id):
        out_user_id = self.find_user_by_username(user_name).user_id
        self.delete_user_talk_room_by_user_id_and_talk_room_id(out_user_id, talk_room_id)

    # 회원 탈퇴한 경우 user테이블 삭제 함수 및 user_talk_room 삭제 함수 호출(user 및 user_talk_room 모두 삭제)
    def delete_withdrawal_user_talk_room_by_username(self, user_name: str):
        withdrawal_user_id = self.delete_user_by_username(user_name).user_id
        self.delete_user_talk_room_by_user_id(withdrawal_user_id)


    def delete_withdrawal_user_talk_room_by_user_id(self, user_id):
        self.delete_user_by_user_id(user_id)
        self.delete_user_talk_room_by_user_id(user_id)

    # talk_room 테이블=======================================================
    # 새로운 채팅방 생성
    def insert_talk_room(self, talk_room_obj: TalkRoom):
        talk_room_name = talk_room_obj.talk_room_name
        open_time_stamp = talk_room_obj.open_time_stamp
        c = self.start_conn()
        c.execute('insert into talk_room (talk_room_name, open_time_stamp) values (?, ?)',
                  (talk_room_name, open_time_stamp))
        self.commit_db()
        created_talk_room = c.execute('select * from talk_room order by talk_room_id desc limit 1').fetchone()
        created_talk_room_obj = TalkRoom(*created_talk_room)
        self.end_conn()
        return created_talk_room_obj

    # def create_talk_room(self, talk_room_name, open_time_stamp):
    #     c = self.start_conn()
    #     c.execute('insert into talk_room (talk_room_name, open_time_stamp) values (?, ?)',
    #               (talk_room_name, open_time_stamp))
    #     self.commit_db()
    #     created_talk_room = c.execute('select * from talk_room order by talk_room_id desc limit 1').fetchone()
    #     created_talk_room_obj = TalkRoom(*created_talk_room)
    #     self.end_conn()
    #     return created_talk_room_obj

    def find_all_talk_room(self):
        c = self.start_conn()
        all_talk_room_obj_list = list()
        all_talk_room_rows = c.execute('select * from talk_room').fetchall()
        for talk_room_row in all_talk_room_rows:
            talk_room_obj = TalkRoom(*talk_room_row)
            all_talk_room_obj_list.append(talk_room_obj)
        self.end_conn()
        return all_talk_room_obj_list

    # talk_room_id로 talk_room_obj 반환 : user_talk_room_obj 의 talk_room_id로 talk_room_obj 반환가능
    def find_talk_room_by_talk_room_id(self, talk_room_id):
        c = self.start_conn()
        row_data = c.execute('select * from talk_room where talk_room_id = ?', (talk_room_id,)).fetchone()
        talk_room_obj = TalkRoom(*row_data)
        self.end_conn()
        return talk_room_obj

    # def find_talk_room_by_talk_room_id(self, user_talk_room_obj: UserTalkRoom):
    #     c = self.start_conn()
    #     talk_room_id = user_talk_room_obj.talk_room_id
    #     row_data = c.execute('select * from talk_room where talk_room_id = ?', (talk_room_id,)).fetchone()
    #     talk_room_obj = TalkRoom(*row_data)
    #     self.end_conn()
    #     return talk_room_obj

    # message테이블===================================================================================
    def insert_message(self, sender_user_id, talk_room_id, send_time_stamp, contents=None, long_contents_id=None):
        c = self.start_conn()
        c.execute('''insert into message (sender_user_id, talk_room_id, send_time_stamp, contents, long_contents_id)
        values (?, ?, ?, ?, ?)''', (sender_user_id, talk_room_id, send_time_stamp, contents, long_contents_id))
        self.commit_db()
        inserted_message_row = c.execute('select * from message order by message_id desc limit 1').fetchone()
        sender_user_obj = self.find_user_by_user_id(sender_user_id)
        inserted_message_obj = Message(*inserted_message_row, sender_user_obj)
        self.end_conn()
        return inserted_message_obj

 	 # 메세지 객체 받아 db에 저장
    def create_message(self, message_obj: Message):
        sender_user_id = message_obj.sender_user_id
        talk_room_id = message_obj.talk_room_id
        send_time_stamp = message_obj.send_time_stamp
        contents = message_obj.contents
        long_contents_id = message_obj.long_contents_id
        inserted_message_obj = self.insert_message(sender_user_id, talk_room_id, send_time_stamp, contents, long_contents_id)
        return inserted_message_obj

    def find_message_by_message_id(self, message_id):
        c = self.start_conn()
        message_row = c.execute('select * from message where message_id = ?', (message_id,)).fetchone()
        sender_user_id = message_row[1]
        sender_user_obj = self.find_user_by_user_id(sender_user_id)
        message_obj = Message(*message_row, sender_user_obj)
        self.end_conn()
        return message_obj

    def find_message_by_sender_user_id(self, sender_user_id) -> list[Message]:
        c = self.start_conn()
        message_rows = c.execute('select * from message where sender_user_id = ?', (sender_user_id,)).fetchall()
        message_obj_list = list()
        for message_row in message_rows:
            sender_user_obj = self.find_user_by_user_id(sender_user_id)
            message_obj = Message(*message_row, sender_user_obj)
            message_obj_list.append(message_obj)
        self.end_conn()
        return message_obj_list

    def find_message_by_talk_room_id(self, talk_room_id) -> list[Message]:
        c = self.start_conn()
        message_rows = c.execute('select * from message where talk_room_id = ?', (talk_room_id,)).fetchall()
        message_obj_list = list()
        for message_row in message_rows:
            sender_user_id = message_row[1]
            sender_user_obj = self.find_user_by_user_id(sender_user_id)
            message_obj = Message(*message_row, sender_user_obj)
            message_obj_list.append(message_obj)
        self.end_conn()
        return message_obj_list

    # long_contents 테이블==========================================================================================
    # contents_type - 0 : long_text, 1: image
    def insert_long_contents(self, contents_type, long_text=None, image=None):
        c = self.start_conn()
        if contents_type == 0 and long_text is not None:
            c.execute('insert into long_contents (contents_type, long_text) values (?, ?)', (contents_type, long_text))
            self.commit_db()
            inserted_long_contents = c.execute(
                'select * from long_contents order by long_contents_id desc limit 1').fetchone()
            inserted_long_contents_obj = LongContents(*inserted_long_contents)
            self.end_conn()
            return inserted_long_contents_obj

        elif contents_type == 0 and long_text is None:
            return f"콘텐츠타입{contents_type} 과 롱텍스트{long_text} 불일치, 이미지{image}"

        elif contents_type == 1 and image is not None:
            c.execute('insert into long_contents (contents_type, image) values (?, ?)', (contents_type, image))
            self.commit_db()
            inserted_long_contents = c.execute(
                'select * from long_contents order by long_contents_id desc limit 1').fetchone()
            inserted_long_contents_obj = LongContents(*inserted_long_contents)
            self.end_conn()
            return inserted_long_contents_obj

        elif contents_type == 1 and image is None:
            return f"콘텐츠타입{contents_type} 과 이미지{image} 불일치, 롱텍스트{long_text}"

    def create_long_contents(self, long_contents_obj:LongContents):
        contents_type = long_contents_obj.contents_type
        long_text = long_contents_obj.long_text
        image = long_contents_obj.image
        inserted_long_contents_obj = self.insert_long_contents(contents_type, long_text=long_text, image=image)
        return inserted_long_contents_obj

    def find_long_contents_by_long_contents_id(self, long_contents_id):
        c = self.start_conn()
        long_contents_row = c.execute('select * from long_contents where long_contents_id = ?', (long_contents_id,)).fetchone()
        long_contents_obj = LongContents(*long_contents_row)
        self.end_conn()
        return long_contents_obj
  # ui 우선 사용 함수=============================================================================================
    # 사용자 아이디 중복 확인
    def assert_same_login_id(self, inserted_id):
        c = self.start_conn()

        username_id = c.execute('select * from user where username = ?', (inserted_id,)).fetchone()
        if username_id is None:
            print('사용 가능한 아이디 입니다.')  # 사용 가능 아이디
            return True
        else:
            print('사용 불가능한 아이디 입니다.')  # 사용불가
            return False

    # 회원가입용 함수(insert_user함수 호출)
    def user_sign_up(self, insert_id, insert_pw, nickname):
        useable_id = self.assert_same_login_id(insert_id)
        if useable_id is False:
            return False
        c = self.start_conn()
        last_user_row = c.execute('select * from user order by user_id desc limit 1').fetchone()
        if last_user_row is None:
            user_id = 1
        else:
            user_id = last_user_row[0] + 1
        sign_up_user_obj = User(user_id, insert_id, insert_pw, nickname)
        self.end_conn()
        sing_up_obj = self.insert_user(sign_up_user_obj)
        return sing_up_obj

    # 사용자 로그인 함수
    def user_log_in(self, login_id, login_pw):
        c = self.start_conn()
        exist_user = c.execute('select * from user where username = ? and password = ?',
                               (login_id, login_pw)).fetchone()
        self.end_conn()
        if exist_user is not None:
            print('로그인 성공')
            login_user_obj = User(*exist_user)
            return login_user_obj
        else:
            print('아이디 혹은 비밀번호를 잘못 입력했습니다.')
            return False

    # 해당 talkroom에 존재하지 않는 user 반환 객체 리스트 반환(초대 가능한 사람 리스트)
    def uninvited_users_from_talk_room(self, talk_room_id):
        c = self.start_conn()
        invited_user = c.execute('select * from user_talk_room where talk_room_id = ?', (talk_room_id,)).fetchall()
        invited_user_id = list()
        for user in invited_user:
            invited_user_id.append(user[1])
        print(invited_user_id)
        uninvited_user_obj_list = list()
        uninvited_user_rows = c.execute("select * from user where user_id not in (" + ",".join("?" * len(invited_user_id)) + ")", invited_user_id).fetchall()
        for un_user_row in uninvited_user_rows:
            un_user_obj = User(*un_user_row)
            uninvited_user_obj_list.append(un_user_obj)
        self.end_conn()
        return uninvited_user_obj_list

 

#Code/domain/class_long_contents.py
import json


class LongContents:
    def __init__(self, contents_id, contents_type, long_text, image):
        self.contents_id = contents_id
        self.contents_type = contents_type
        self.long_text = long_text
        self.image = image

    def toJSON(self):
        return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True)

    def __repr__(self):
        return f'{self.__dict__}'

    def __eq__(self, other):
        if isinstance(other, LongContents) and \
                self.contents_id == other.contents_id and \
                self.contents_type == other.contents_type and \
                self.long_text == other.long_text and \
                self.image == other.image:
            return True
        return False
# Code/domain/class_message.py

import json


class Message:
    def __init__(self, message_id, sender_user_id, talk_room_id, send_time_stamp, contents, long_contents_id, user_obj=None):
        self.message_id = message_id
        self.sender_user_id = sender_user_id
        self.talk_room_id = talk_room_id
        self.send_time_stamp = send_time_stamp
        self.contents = contents
        self.long_contents_id = long_contents_id
        self.user_obj = user_obj

    def toJSON(self):

        obj_str = json.dumps(self, default=lambda o: o.__dict__)
        return obj_str


    def __repr__(self):
        return f'{self.__dict__}'

    def __eq__(self, other):
        if isinstance(other, Message) and \
                self.message_id == other.message_id and \
                self.sender_user_id == other.sender_user_id and \
                self.talk_room_id == other.talk_room_id and \
                self.send_time_stamp == other.send_time_stamp and \
                self.contents == other.contents and \
                self.long_contents_id == other.long_contents_id:
            return True
        return False
#Code/domain/class_talk_room.py
import json
import datetime


class TalkRoom:
    def __init__(self, talk_room_id, talk_room_name, open_time_stamp):
        self.talk_room_id = talk_room_id
        self.talk_room_name = talk_room_name
        self.open_time_stamp = open_time_stamp
        self.talk_room_user_list = list()

    def get_datetime(self):
        datetime_obj = datetime.datetime.strptime(self.open_time_stamp)
        return datetime_obj

    def __str__(self):
        return f"{self.__repr__()}"

    def __repr__(self):
        return f"{self.__dict__}"

    def toJSON(self):
        origin_data = dict()
        origin_data.update({"talk_room_id": self.talk_room_id})
        origin_data.update({"talk_room_name": self.talk_room_name})
        origin_data.update({"open_time_stamp": self.open_time_stamp})
        result = json.dumps(origin_data, default=lambda o: o.__dict__)
        return result

    def to_dict(self):
        origin_data = dict()
        origin_data.update({"talk_room_id": self.talk_room_id})
        origin_data.update({"talk_room_name": self.talk_room_name})
        origin_data.update({"open_time_stamp": self.open_time_stamp})
        result =f'{origin_data}'
        return result


    def append_user(self, user):
        if isinstance(user, list):
            self.talk_room_user_list.extend(user)
        else:
            self.talk_room_user_list.append(user)


    def __eq__(self, other):
        if isinstance(other, TalkRoom) and \
                self.talk_room_id == other.talk_room_id and \
                self.talk_room_name == other.talk_room_name and \
                self.open_time_stamp == other.open_time_stamp:
            return True
#Code/domain/class_user.py
import json
import random


class User:
    def __init__(self, user_id, username, password, nickname):
        self.user_id = user_id
        self.username = username
        self.password = password
        self.nickname = nickname


    def __str__(self):
        return f"{self.__repr__()}"

    def __repr__(self):
        return f"{self.__dict__}"

    def toJSON(self) -> str:
        return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True)

    def __eq__(self, other):
        if isinstance(other, User) and \
                self.user_id == other.user_id and \
                self.username == other.username and \
                self.password == other.password and \
                self.nickname == other.nickname:
            return True
        return False

    def __lt__(self, other):
        return self.nickname < other.nickname
#Code/domain/class_user_talk_room.py
import json


class UserTalkRoom:
    def __init__(self, user_talk_room_id, user_id, talk_room_id):
        self.user_talk_room_id = user_talk_room_id
        self.user_id = user_id
        self.talk_room_id = talk_room_id


    def __str__(self):
        return f"{self.__repr__()}"

    def __repr__(self):
        return f"{self.__dict__}"

    def toJSON(self):
        return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True)

    def __eq__(self, other):
        if isinstance(other, UserTalkRoom) and \
                self.user_talk_room_id == other.user_talk_room_id and \
                self.user_id == other.user_id and \
                self.talk_room_id == other.talk_room_id:
            return True
        return False
#Code/front/class_client_controller.py
import datetime
import time

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import QPoint, Qt, pyqtSignal

from Code.domain.class_db_connector import DBConnector
from Code.front.class_custom_message_box import NoFrameMessageBox
from Code.front.widget_friend_list_page import FriendListWidget
from Code.front.widget_join_page import JoinWidget
from Code.front.widget_login_page import LoginWidget
from Code.front.widget_talk_room_list_page import TalkRoomListWidget
from Code.front.widget_make_talk_room import InviteFriendListWidget
from Code.front.widget_talk_room_page import TalkRoomWidget
from Code.front.widget_search_talk_room_member_list_page import UserListWidgetInTalkRoom
from Code.front.widget_invite_user_in_chat_room import InviteFriendListWidgetInTalkRoom
from Code.front.widget_profile_page import ProfilePage

from Code.domain.class_user import User
from Code.network.class_client import ClientApp
from Common.class_json import KKOEncoder, KKODecoder
from Common.common_module import get_now_time_str


class WindowController(QtWidgets.QWidget):
    # signal 클래스 변수
    assert_same_id_signal = pyqtSignal(bool)
    sign_up_signal = pyqtSignal(bool)
    log_in_signal = pyqtSignal(bool)
    enter_square_signal = pyqtSignal(bool)
    all_user_list_signal = pyqtSignal(str)
    user_talk_room_signal = pyqtSignal(str)
    talk_room_user_list_se_signal = pyqtSignal(int, str)
    out_talk_room_signal = pyqtSignal(bool)
    send_msg_se_signal = pyqtSignal(str)
    invite_user_talk_room_signal = pyqtSignal(bool)
    make_talk_room_signal = pyqtSignal(int)
    talk_room_msg_signal = pyqtSignal(str)

    def __init__(self, client_app=ClientApp):
        assert isinstance(client_app, ClientApp)
        super().__init__()
        self.client_app = client_app  # db연결 인스턴스
        self.client_app.set_widget(self)
        self.db_connector = None
        # Domain 인스턴스
        self.widget_login_page = LoginWidget(self)  # 로그인 화면 ui 클래스 함수화
        self.widget_friend_list_page = FriendListWidget(self)  # 친구창 ui 클래스 함수화
        self.widget_join = JoinWidget(self)  # 회원 가입창 ui 클래스 함수화
        self.widget_talk_room_list = TalkRoomListWidget(self)
        self.widget_ask_to_make_talk_room = InviteFriendListWidget(self)
        self.widget_add_member_in_chat_room = InviteFriendListWidgetInTalkRoom(self)
        self.widget_profile_window = ProfilePage(self)
        self.widget_talk_room = TalkRoomWidget(self, 1)
        self.widget_room_member_list = UserListWidgetInTalkRoom(self, self.widget_talk_room)
        self.stored_user_list = list()  # 가입되어있는 모든 유저의 정보
        self.stored_talk_room_id_list = list()  # 로그인한 유저의 친구 리스트
        self.talk_room_related_user_id_list = list()
        self.stored_user_obj = None
        self.valid_duplication_id = None  # 중복 체크 여부 확인 변수 -> 기초 false set / 중복확인 후 결과 따라 True 됨
        self.join_id = None  # 회원가입에서 적힌 아이디
        self.join_pw = None  # 회원가입에서 적힌 비밀번호
        self.join_nickname = None  # 회원가입에서 적힌 닉네임
        self.encoder = KKOEncoder()
        self.decoder = KKODecoder()
        self.initial_trigger_setting()

        # ui 동작 관련 변수
        self.list_widget_geometry_x = None
        self.list_widget_geometry_y = None
        self.drag_start_position = QPoint(0, 0)

    def mousePressEvent(self, widget, event):
        self.drag_start_position = QPoint(widget.x(), widget.y())
        if event.button() == Qt.LeftButton:
            self.drag_start_position = event.globalPos() - widget.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, widget, event):
        if event.buttons() == Qt.LeftButton:
            widget.move(event.globalPos() - self.drag_start_position)
            event.accept()
 def get_user_self(self):
        return self.client_app.get_user_self()

    def get_message_list(self, talk_room_id):
        stored_dictionary_message = self.client_app.stored_talk_message
        if talk_room_id not in stored_dictionary_message.keys():
            stored_dictionary_message.update({talk_room_id: list()})
        result_list = stored_dictionary_message[talk_room_id]

        return result_list

    def get_all_user_list(self):
        user_list = self.client_app.get_all_user_list()
        if len(user_list) == 0:
            self.client_app.send_all_user_list()
            time.sleep(0.5)
        return self.client_app.all_user_list_in_memory

    def get_user_by_id(self, user_id):
        return self.client_app.get_user_by_id(user_id)

    def get_talk_room_by_room_id(self, talk_room_id):
        return self.client_app.get_talk_room_by_room_id(talk_room_id)

    def get_already_invited_user_list(self):
        return self.client_app.get_already_invited_user_list()

    def get_not_yet_invited_user_list(self):
        return self.client_app.get_not_yet_invited_user_list()

    def send_make_talk_room_user_id(self, talk_room_name, selected_user_id_list):
        # todo: 채팅방을 만들때 초대할 유저의 정보, 토크룸의 이름을 보낸다
        now_time = get_now_time_str()
        self.client_app.send_make_talk_room(talk_room_name, selected_user_id_list, now_time)

    def uninvited_user_list(self, talk_room_id):
        """
        todo: 만일 채팅방에서 초대되지 않은 인원을 파악하고자 할 때 유저 리스트를 반환함
        :param talk_room_id:
        :return:
        """

    def show_profile_page(self, user_id):
        self.client_app: ClientApp
        selected_user = [x for x in self.client_app.all_user_list_in_memory if x.user_id == user_id]
        selected_user = selected_user[0]
        self.widget_profile_window.set_profile_user_data(selected_user)
        self.widget_profile_window.show()

    def show_talk_room(self, talk_room_id):
        """
        채팅방 보여주는 메서드
        채팅방에 초대 되어있지 않은 유저 목록 저장
        :return:
        """
        print('show try')
        self.client_app.selected_talk_room_id = talk_room_id
        self.widget_talk_room.set_talk_room_id(talk_room_id)
        self.widget_talk_room.show()

    # def get_friend_profile(self):



    def show_member_plus(self):
        """
        채팅방에 멤버추가 위젯 보여주는 메서드
        :return:
        """
        self.widget_add_member_in_chat_room.show()

    def show_room_member_list(self):
        """
        채팅방에 있는 멤버 목록을 보여주는 메서드
        :return:
        """
        self.widget_room_member_list.show()

    def show_make_talk_room(self):
        """
        채팅방 만드는 위젯 보여주는 메서드
        :return:
        """
        self.widget_ask_to_make_talk_room.show()
 def show_talk_room_list_page(self):
        """
        채팅방 보여주는 메서드
        :return:
        """
        cp = self.widget_friend_list_page.frameGeometry().topLeft()
        cp: QPoint
        self.list_widget_geometry_x = cp.x()
        self.list_widget_geometry_y = cp.y()
        self.widget_talk_room_list.setGeometry(self.list_widget_geometry_x, self.list_widget_geometry_y,
                                               self.widget_talk_room_list.width(),
                                               self.widget_talk_room_list.width())
        self.widget_talk_room_list.show()
    def show_friend_list(self):
        """
        친구창 보여주는 메서드
        :return:
        """
        cp = self.widget_talk_room_list.frameGeometry().topLeft()
        cp: QPoint
        self.list_widget_geometry_x = cp.x()
        self.list_widget_geometry_y = cp.y()
        self.widget_friend_list_page.setGeometry(self.list_widget_geometry_x, self.list_widget_geometry_y,
                                                 self.widget_friend_list_page.width(),
                                                 self.widget_friend_list_page.height())
        self.widget_friend_list_page.show()
    # 채팅방 리스트 화면 띄우는 메서드
    def show_join_page(self):
        """
        회원 가입창 띄우는 메서드
        """
        self.widget_join.show()

    def run(self):
        """
        로그인 화면 띄우는 메서드
        """
        self.widget_login_page.show()

    def show_login_success(self):
        # todo: 친구창과 채팅방을 띄울때 본인의 정보를 따로 처리해야한다
        self.widget_friend_list_page.show()  # 친구창 띄우는 함수


    @staticmethod
    def clear_widget(widget):
        if widget.layout() is not None:
            while widget.layout().count() > 0:
                item = widget.layout().takeAt(0)
                if item.widget():
                    item.widget().deleteLater()

    # 1대1대화 시 해당 멤버가 속한 방 talk_room_id 찾기
    def find_already_created_room(self, target_user_id):
        talk_room_list = self.get_all_talk_room()
        user_self_id = self.get_user_self().user_id
        for talk_room in talk_room_list:
            talk_room_user_list = talk_room.talk_room_user_list
            if len(talk_room_user_list) == 2 and [user_self_id, target_user_id] in talk_room_user_list:
                return talk_room.talk_room_id
        else:
            return False

    # 레이아웃 안에있는 위젯들 삭제
    def open_one_to_one_chat_room(self, target_user_id):
        found_talk_room_id = self.find_already_created_room(target_user_id)
        print('found id', found_talk_room_id)
        if found_talk_room_id is False:
            target_user_obj = self.get_user_by_id(target_user_id)
            invite_list = list()
            invite_list.append(self.get_user_self().user_id)  # 본인 포함이므로
            invite_list.append(target_user_id)
            now_time_stamp = get_now_time_str()
            self.client_app.send_make_talk_room(target_user_obj.nickname, invite_list, now_time_stamp)

        else:
            self.show_talk_room(found_talk_room_id)

    def open_talk_room_widget_after_recv_talk_room_id(self, talk_room_id: int = None):
        self.show_talk_room(talk_room_id)
    # ==== 클라이언트 response 함수 ================================================================
    def initial_trigger_setting(self):
        self.valid_duplication_id = False
        self.assert_same_id_signal.connect(self.assert_same_name_res)
        self.sign_up_signal.connect(self.sign_up_res)
        self.log_in_signal.connect(self.log_in_res)
        self.enter_square_signal.connect(self.enter_square_res)
        self.all_user_list_signal.connect(self.all_user_list_res)
        self.user_talk_room_signal.connect(self.user_talk_room_list_res)
        self.talk_room_user_list_se_signal.connect(self.talk_room_user_list_se_res)
        self.out_talk_room_signal.connect(self.out_talk_room_res)
        self.send_msg_se_signal.connect(self.send_msg_se_res)
        self.invite_user_talk_room_signal.connect(self.invite_user_talk_room_res)
        self.make_talk_room_signal.connect(self.make_talk_room_res)
        self.talk_room_msg_signal.connect(self.load_message_history)

    # client function =================================
    # 클라 -> 서버 아이디 중복 체크 요청

    def assert_same_username(self, join_username):  # 아이디 중복
        self.client_app.send_join_id_for_assert_same_username(join_username)

    def assert_same_name_res(self, return_result: bool):
        if return_result is True:
            self.valid_duplication_id = True
            return NoFrameMessageBox(self, "가능", "중복 없는 아이디, 써도됌", "about")
        elif return_result is False:
            return NoFrameMessageBox(self, "불가능", "중복 아이디, 새로 쓰기", "about")

        # 클라 -> 서버 회원가입 요청

    def join_access(self):
        join_username = self.widget_join.lineEdit_join_username.text()
        join_pw = self.widget_join.lineedit_join_pw.text()
        join_nickname = self.widget_join.lineedit_join_user_nickname.text()
        self.client_app.send_join_id_and_pw_for_join_access(join_username, join_pw, join_nickname)

        # 서버 -> 클라 회원가입 결과 체크 결과 대응

    def sign_up_res(self, return_result: bool):
        if return_result is True:
            result = NoFrameMessageBox(self, "성공", "회원가입 성공", "about")
            self.widget_join.close()
            return
        elif return_result is False:
            return NoFrameMessageBox(self, "실패", "회원가입 실패", "about")

        #  클라 -> 서버 로그인 요청

    def assert_login_data(self, login_id, login_pw):
        """
        기능1:
        로그인 화면에서 로그인 승인 버튼에 시그널을 주면 서버에 ID,PW가 저장 되있고 일치여부 요청
        """
        self.client_app.user_id = None
        self.client_app.username = login_id
        self.client_app.user_pw = login_pw
        self.client_app.user_nickname = None
        self.client_app.send_login_id_and_pw_for_login_access(login_id, login_pw)

    def log_in_res(self, return_result: bool):
        if return_result is True:
            # 모든건 로그인 버튼을 누르면 시작한다. 나중에 수정
            # 받아와야할 정보 : 전체 회원 리스트, 본인이 포함된 톡방 리스트
            login_user = self.get_user_self()
            self.widget_friend_list_page.login_user_obj = login_user
            self.all_user_list_req()
            self.user_talk_room_list_req()
            time.sleep(0.05)
            self.show_login_success()
            # self.out_talk_room()
            return NoFrameMessageBox(self, "성공", "login 성공", "about")
        elif return_result is False:
            return NoFrameMessageBox(self, "실패", "login 실패", "about")

        # 클라 -> 서버 초기 체팅방 입장, 로그인시 실행

    def enter_square(self):
        self.client_app.send_enter_square()
        # 전체 회원방 메시지 요청

        # 서버 -> 클라 초기 체팅방 입장 결과 체크
  def enter_square_res(self):
        # 화면 띄우기? 화면전환?
        # 전체 회원방 메시지 저장
        print("초기방 입장 완료")

        # 클라 -> 서버 유저 리스트 요청, 로그인시 할 수도있음

    def all_user_list_req(self):
        self.client_app.send_all_user_list()

        # 서버 -> 클라 유저 리스트 정보 받음

    def all_user_list_res(self, return_result: str):
        user_list = self.decoder.decode_any(return_result)
        self.widget_login_page.close()
        self.client_app.all_user_list_in_memory = user_list
        self.widget_friend_list_page.show()
        # 클라 -> 서버 채팅방 리스트 요청

    def get_all_talk_room(self):
        return self.client_app.talk_room_list_in_memory

    def get_talk_by_room_id(self, talk_room_id):
        return self.client_app.get_talk_by_room_id(talk_room_id)

    def user_talk_room_list_req(self):
        self.client_app.send_user_talk_room_list()

        # 서버 -> 클라 채팅방 리스트 정보 받음

    def user_talk_room_list_res(self, return_result: str):
        result_list = self.decoder.decode_any(return_result)
        for talk_room in result_list:
            self.talk_room_user_list_se(talk_room.talk_room_id)
            self.client_app.send_talk_room_msg(talk_room.talk_room_id)
        self.widget_talk_room_list.talk_room_list = self.client_app.talk_room_list_in_memory.copy()
        self.widget_talk_room_list.refresh_chat_room_list()

        # 클라 -> 서버 채팅방 관련 유저 정보 요청
        # 방 아이디를 넘겨줘야 할듯 하다.

    def talk_room_user_list_se(self, talk_room_id):
        self.client_app.send_talk_room_user_list_se(talk_room_id)

        # 서버 -> 클라 톡방 유저 객체 정보 획득

    def talk_room_user_list_se_res(self, talk_room_id:int, return_result: str):
        related_talk_room_user_list = self.decoder.decode_any(return_result)
        found_talk_room = None
        if len(self.get_all_talk_room()) == 0:
            time.sleep(0.5)
        for talk_room in self.get_all_talk_room():
            temp_num = talk_room.talk_room_id
            if temp_num == talk_room_id:
                found_talk_room = talk_room
                break
        if found_talk_room is None:
            raise "채팅방 못찾음"
        found_talk_room.talk_room_user_list.clear()
        found_talk_room.append_user(related_talk_room_user_list)
        print(found_talk_room, end="!!!")
        print(found_talk_room.talk_room_user_list)

    def out_talk_room(self):
        self.client_app.send_out_talk_room(talk_room_id)

        # 채팅방 나가기 결과 반환
        # 메세지 박스를 화면 전환 해주세요
   def out_talk_room_res(self, return_result: bool):
        if return_result is True:
            return NoFrameMessageBox(self, "성공", "방탈출 성공", "about")
        elif return_result is False:
            return NoFrameMessageBox(self.widget_join, "실패", "방탈출 실패", "about")
        # 화면 전환후 채팅방 목록 불러오기

        # 클라 -> 서버 메시지 전달

    def send_msg_se(self, talk_room_id, txt_message):
        self.client_app.send_send_msg_se(talk_room_id, txt_message)

        # 서버 -> 클라 메시지 받기

    def send_msg_se_res(self, return_result: str):
        message = self.decoder.decode_any(return_result)
        self.client_app.store_message(message)
        self.widget_talk_room.show()
        # todo: send 메시지

        # 클라 -> 서버 단톡방 초대 요청

    def invite_user_talk_room_res(self, return_result: bool):
        self.show_talk_room(self.client_app.selected_talk_room_id)

        # 채팅방 개설하기

    def make_talk_room_res(self, return_result: int):
        self.show_talk_room(return_result)

    def load_message_history(self, return_result: str):
        # self.widget_talk_room.hide()
        # self.widget_talk_room.show()
        print('메시지 저장 완료')

    def send_file_to_chat_room(self):
        save_excel_dialog = NoFrameMessageBox(self, "파일 업로드", "파일을 업로드합니까?", "question").result
        if save_excel_dialog is True:
            save_path_file_name, _, = QtWidgets.QFileDialog.getSaveFileName(self, '파일 저장', './')
            print(f"{save_path_file_name} send 로직 실행")
        # todo: send 메시지
# Code/network/class_client.py
import datetime
import socket
import time
from threading import *

from Code.domain.class_message import Message
from Code.domain.class_talk_room import TalkRoom
from Code.domain.class_user import User
from Code.domain.class_user_talk_room import UserTalkRoom
from Common.class_json import KKODecoder


class ClientApp:
    HOST = '127.0.0.1'
    PORT = 9999
    BUFFER = 50000
    FORMAT = "utf-8"
    HEADER_LENGTH = 30

    assert_username = "assert_username"
    join_user = "join_user"
    login = "login"
    enter_square = "enter_square"
    all_user_list = "all_user_list"
    user_talk_room_list = "user_talk_room_list"
    talk_room_user_list_se = 'talk_room_user_list_se'
    out_talk_room = 'out_talk_room'
    send_msg_se = 'send_msg_se'
    invite_user_talk_room = 'invite_user_talk_room'
    make_talk_room = 'make_talk_room'
    talk_room_msg = 'talk_room_msg'
    send_msg_c_room = "send_msg_c_room"
    send_alarm_c_room = "send_alarm_c_room"
    send_ = "send_alarm_c_room"

    HEADER_LIST = {
        assert_username: assert_username.encode(FORMAT),
        join_user: join_user.encode(FORMAT),
        login: login.encode(FORMAT),
        send_msg_c_room: send_msg_c_room.encode(FORMAT),
        send_alarm_c_room: send_alarm_c_room.encode(FORMAT),
    }

    def __init__(self):
        # 서버 소켓 설정
        self.client_socket = None
        self.config = None
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client_socket.connect((self.HOST, self.PORT))
        # self.client_socket.setblocking(False)
        self.user_id = None
        self.user_pw = None
        self.username = None
        self.user_nickname = None
        self.stored_talk_message = dict()

        self.receive_thread = Thread(target=self.receive_message)
        self.receive_thread.daemon = True
        self.receive_thread.start()
        self.talk_room_list_in_memory = list()
        self.client_widget = None
        self.decoder = KKODecoder()
        self.all_user_list_in_memory = list()
        self.selected_talk_room_id = 1
        self.buffer_guest_list = None

        #

        # client function =================================

    def set_widget(self, widget_):
        self.client_widget = widget_

    def get_user_self(self):
        return User(self.user_id, self.username, self.user_pw, self.user_nickname)

 def get_user_by_id(self, user_id):
        find_list = [x for x in self.all_user_list_in_memory if x.user_id == user_id]
        found_user = find_list[0]
        return found_user

    def get_all_user_list(self):
        return self.all_user_list_in_memory.copy()

    def get_not_yet_invited_user_list(self):
        total_user_list = self.get_all_user_list().copy()
        now_talk_room_id = self.selected_talk_room_id
        talk_room_obj = self.get_talk_room_by_room_id(now_talk_room_id)
        user_list = talk_room_obj.talk_room_user_list.copy()
        result_list = [x for x in total_user_list if x not in user_list]
        for user in result_list:
            print(user.user_id)
        return result_list

    def get_already_invited_user_list(self):
        now_talk_room_id = self.selected_talk_room_id
        talk_room_obj = self.get_talk_room_by_room_id(now_talk_room_id)
        user_list = talk_room_obj.talk_room_user_list.copy()
        return user_list

    def get_talk_room_by_room_id(self, talk_room_id: int):
        assert isinstance(talk_room_id, int)
        result = None
        for talk_room in self.talk_room_list_in_memory:
            temp_num = talk_room.talk_room_id
            if temp_num == talk_room_id:
                result = talk_room
                break
        if result is None:
            raise '결과 찾기 실패'
        found_talk_room = result
        return found_talk_room

    def send_join_id_for_assert_same_username(self, input_username: str):
        data_msg = f"{input_username:<{self.BUFFER - self.HEADER_LENGTH}}".encode(self.FORMAT)
        data_msg_length = len(data_msg)
        request_msg = self.assert_username
        header_msg = f"{request_msg:<{self.HEADER_LENGTH}}".encode(self.FORMAT)
        self.client_socket.send(header_msg + data_msg)  # 헤더를 붙이고 보내는 동작(?)

    def send_join_id_and_pw_for_join_access(self, join_username, join_pw, join_nickname):
        join_user = User(None, join_username, join_pw, join_nickname)
        user_json_str = join_user.toJSON()
        request_msg = self.join_user
        result = self.fixed_volume(request_msg, user_json_str)
        self.client_socket.send(result)

    def send_login_id_and_pw_for_login_access(self, login_username, login_pw):
        # 로그인 파일이 있다고 가정하고 제작
        login_user = User(None, login_username, login_pw, None)
        login_user_str = login_user.toJSON()
        request_msg = self.login
        result = self.fixed_volume(request_msg, login_user_str)
        self.client_socket.send(result)  # 응답 받기

    # 로그인시 가져와야하는 정보들을 한번에 묶을수 있을까? 헤더 그리고 반환값이 달라서 문제임
    def send_enter_square(self):
        user_object = User(self.user_id, self.username, self.user_pw, self.user_nickname)
        user_object_str = user_object.toJSON()
        request_msg = self.enter_square
        result = self.fixed_volume(request_msg, user_object_str)
        self.client_socket.send(result)

    def send_all_user_list(self):
        user_object = User(self.user_id, self.username, self.user_pw, self.user_nickname)
        user_object_str = user_object.toJSON()
        request_msg = self.all_user_list
        result = self.fixed_volume(request_msg, user_object_str)
        self.client_socket.send(result)

    def send_user_talk_room_list(self):
        user_object = User(self.user_id, self.username, self.user_pw, self.user_nickname)
        user_object_str = user_object.toJSON()
        request_msg = self.user_talk_room_list
        result = self.fixed_volume(request_msg, user_object_str)
        self.client_socket.send(result)

    def send_talk_room_user_list_se(self, talk_room_id):
        """
        해당 방 정보 유저 갱신
        :param talk_room_id:
        :return:
        """
        self.selected_talk_room_id = talk_room_id  # 해당 선택된 톡방 저장
        user_talk_room_obj = UserTalkRoom(None, self.user_id, talk_room_id)
        user_talk_room_obj_str = user_talk_room_obj.toJSON()
        request_msg = self.talk_room_user_list_se
        result = self.fixed_volume(request_msg, user_talk_room_obj_str)
        self.client_socket.send(result)

    def send_out_talk_room(self, talk_room_id):
        user_talk_room_obj = UserTalkRoom(None, self.user_id, talk_room_id)
        user_talk_room_obj_str = user_talk_room_obj.toJSON()
        request_msg = self.out_talk_room
        result = self.fixed_volume(request_msg, user_talk_room_obj_str)
        self.client_socket.send(result)

    def send_send_msg_se(self, talk_room_id, msg):
        msg_obj = Message(None, self.user_id, talk_room_id, str(datetime.datetime.now()), msg, None,
                          User(self.user_id, self.username, self.user_pw, self.user_nickname))
        # self.store_message(msg_obj)
        msg_obj_str = msg_obj.toJSON()
        request_msg = self.send_msg_se
        result = self.fixed_volume(request_msg, msg_obj_str)
        self.client_socket.send(result)

    def send_invite_user_talk_room(self, talk_room_id, invite_user):
        user_talk_room_obj = UserTalkRoom(None, invite_user, talk_room_id)
        user_talk_room_obj_str = user_talk_room_obj.toJSON()
        request_msg = self.invite_user_talk_room
        result = self.fixed_volume(request_msg, user_talk_room_obj_str)
        self.client_socket.send(result)

    def send_make_talk_room(self, room_name, guest_user_id_list: list[int], open_time_stamp):
        """2번에 나눠서 로직 구성됨, 1) 방 개설 / 2) 방 입장시키기"""
        self.buffer_guest_list = guest_user_id_list
        create_room = TalkRoom(None, room_name, open_time_stamp)
        create_room_str = create_room.toJSON()
        reqeust_msg = self.make_talk_room
        result = self.fixed_volume(reqeust_msg, create_room_str)
        self.client_socket.send(result)

    def invite_guest_user(self, talk_room_id):
        # 방 아이디를 부여받아야함. talk_room_id 부여 받고 스레드상에서 발동됨
        guest_user_id_list = self.buffer_guest_list
        for guest_id in guest_user_id_list:
            self.send_invite_user_talk_room(talk_room_id, guest_id)
        self.send_talk_room_user_list_se(talk_room_id)
        self.buffer_guest_list.clear()

    def send_talk_room_msg(self, talk_room_id):
        user_talk_room_obj = UserTalkRoom(None, self.user_id, talk_room_id)
        user_talk_room_obj_str = user_talk_room_obj.toJSON()
        reqeust_msg = self.talk_room_msg
        result = self.fixed_volume(reqeust_msg, user_talk_room_obj_str)
        self.client_socket.send(result)

    # 크기 고정으로 만들어 주는 함수
    def fixed_volume(self, header, data):
        header_msg = f"{header:<{self.HEADER_LENGTH}}".encode(self.FORMAT)
        data_msg = f"{data:<{self.BUFFER - self.HEADER_LENGTH}}".encode(self.FORMAT)
        return header_msg + data_msg

    def store_message(self, message_obj):
        """
        클라이언트 stored_talk_message 변수에 메시지를 저장하는 함수
        :param message_obj:
        :return:
        """
        talk_room_id = message_obj.talk_room_id

        if talk_room_id not in self.stored_talk_message.keys():
            self.stored_talk_message.update({talk_room_id: list()})

        target_message_list = self.stored_talk_message[talk_room_id]
        target_message_list.append(message_obj)

    def send_file_to_chat_room(self):
        # todo: send 메시지
        pass

    def receive_message(self):
        while True:
            # self.return_result = self.client_socket.recv(self.BUFFER).decode(self.FORMAT)
            return_result = self.client_socket.recv(self.BUFFER).decode(self.FORMAT)
            response_header = return_result[:self.HEADER_LENGTH].strip()
            response_data = return_result[self.HEADER_LENGTH:].strip()
            print(f"CLIENT RECEIVED: ({response_header},{response_data})")
            # 아이디 중복 확인 결과
            if response_header == self.assert_username:
                if response_data == 'pass':
                    self.client_widget.assert_same_id_signal.emit(True)
                elif response_data == '.':
                    self.client_widget.assert_same_id_signal.emit(False)
            # 회원 가입 중복 확인 결과
            elif response_header == self.join_user:
                if response_data == 'pass':
                    self.client_widget.sign_up_signal.emit(True)
                elif response_data == '.':
                    self.client_widget.sign_up_signal.emit(False)
            # 로그인 신청 확인 결과, 로그인한 유저정보 저장?
            elif response_header == self.login:
                if response_data == '.':
                    self.client_widget.log_in_signal.emit(False)
                else:
                    object_data = self.decoder.decode_any(response_data)
                    self.username = object_data.username
                    self.user_id = object_data.user_id
                    self.user_pw = object_data.password
                    self.user_nickname = object_data.nickname
                    self.client_widget.log_in_signal.emit(True)
            # 초기 단톡방 입장 반환
            elif response_header == self.enter_square:
                if response_data == 'pass':
                    self.client_widget.enter_square_signal.emit(True)
            # 본인 제외 모든 유저 정보
            elif response_header == self.all_user_list:
                if response_data == '.':
                    print('오류?')
                else:
                    self.all_user_list_in_memory = self.decoder.decode_any(response_data)
                    self.client_widget.all_user_list_signal.emit(response_data)
            # 채팅방 리스트 정보
            elif response_header == self.user_talk_room_list:
                self.talk_room_list_in_memory = self.decoder.decode_any(response_data)
                for talk_room_obj in self.talk_room_list_in_memory:
                    self.send_talk_room_user_list_se(talk_room_obj.talk_room_id)
                self.client_widget.user_talk_room_signal.emit(response_data)

            # 채팅방 참여 유저 정보
            elif self.talk_room_user_list_se in response_header:
                if response_data == '.':
                    print('아무도 없는 방')
                else:
                    if len(self.talk_room_list_in_memory) == 0:
                        time.sleep(0.05)  # 정보가 받아질 때까지 대기
                    try:

                        header_str, talk_room_id_str = response_header.split("%")
                        print(header_str, talk_room_id_str)
                        talk_room_id = int(talk_room_id_str.strip())
                        self.client_widget.talk_room_user_list_se_signal.emit(talk_room_id, response_data)
                    except:
                        return
            # 방나가기
            elif response_header == self.out_talk_room:
                if response_data == 'pass':
                    self.client_widget.out_talk_room_signal.emit(True)
                elif response_data == '.':
                    self.client_widget.out_talk_room_signal.emit(False)
            # 메시지 받기
            elif response_header == self.send_msg_se:
                msg_obj = self.decoder.decode_any(response_data)
                # if msg_obj.sender_user_id == self.user_id:
                #     pass
                # else:
                #     self.client_widget.send_msg_se_signal.emit(response_data)
                self.client_widget.send_msg_se_signal.emit(response_data)

            # 상대방 초대
            elif response_header == self.invite_user_talk_room:
                if response_data == 'pass':
                    self.client_widget.invite_user_talk_room_signal.emit(True)
                elif response_data == '.':
                    self.client_widget.invite_user_talk_room_signal.emit(False)
            # 방 만들기
            elif response_header == self.make_talk_room:
                talk_room_obj = self.decoder.decode_any(response_data)
                self.invite_guest_user(talk_room_obj.talk_room_id)
                self.talk_room_list_in_memory.append(talk_room_obj)
                self.client_widget.make_talk_room_signal.emit(talk_room_obj.talk_room_id)

            # 메시지 받아보기
            elif response_header == self.talk_room_msg:
                message_list = self.decoder.decode_any(response_data)
                for m in message_list:
#Code/network/class_client_prototype.py

from threading import Thread

from PyQt5 import QtCore, QtGui, QtWidgets

from Code.domain.class_db_connector import DBConnector
from Code.domain.class_user import User
from Code.network.class_worker_thread import WorkerServerThread
from Code.network.server_ui.ui_chat_room import Ui_prototype
from Code.network.server_ui.ui_server_controller_widget import Ui_server_controller
from Common.class_json import KKODecoder, KKOEncoder
from Common.common_module import *
from PyQt5.QtCore import pyqtSignal


class ClientPrototypeWidget(QtWidgets.QWidget, Ui_prototype):
    ENCODED_DOT = bytes('.', 'utf-8')
    ENCODED_PASS = bytes('pass', 'utf-8')

    # signal 클래스 변수

    assert_same_id_signal = pyqtSignal(bool)
    sign_up_signal = pyqtSignal(bool)
    log_in_signal = pyqtSignal(bool)
    enter_square_signal = pyqtSignal(bool)
    all_user_list_signal = pyqtSignal(str)
    user_talk_room_signal = pyqtSignal(str)
    talk_room_user_list_se_signal = pyqtSignal(int, str)
    out_talk_room_signal = pyqtSignal(bool)
    send_msg_se_signal = pyqtSignal(str)
    invite_user_talk_room_signal = pyqtSignal(bool)
    make_talk_room_signal = pyqtSignal(int)
    talk_room_msg_signal = pyqtSignal(str)

    def __init__(self, client_app):
        super().__init__()
        self.setupUi(self)
        self.client_app = client_app
        self.valid_duplication_id = False
        self.qthread = WorkerServerThread(self)
        self.set_btn_trigger()
        self.set_init_label()
        self.encoder = KKOEncoder()
        self.decoder = KKODecoder()
        self.set_client_know_each_other()

        self.assert_same_id_signal.connect(self.assert_same_name_res)
        self.sign_up_signal.connect(self.sign_up_res)
        self.log_in_signal.connect(self.log_in_res)
        self.enter_square_signal.connect(self.enter_square_res)
        self.all_user_list_signal.connect(self.all_user_list_res)
        self.user_talk_room_signal.connect(self.user_talk_room_list_res)
        self.talk_room_user_list_se_signal.connect(self.talk_room_user_list_se_res)
        self.out_talk_room_signal.connect(self.out_talk_room_res)
        self.send_msg_se_signal.connect(self.send_msg_se_res)
        self.invite_user_talk_room_signal.connect(self.invite_user_talk_room_res)
        self.make_talk_room_signal.connect(self.make_talk_room_res)
        self.talk_room_msg_signal.connect(self.talk_room_msg_res)

    def set_client_know_each_other(self):
        self.client_app.set_widget(self)

    def set_init_label(self):
        self.initialize_app()
        self.setWindowTitle("성혁이를 위한 프로토타입 위젯")

    def set_btn_trigger(self):
        self.btn_init.clicked.connect(lambda state: self.initialize_app())
        self.btn_check_same_id.clicked.connect(lambda state: self.assert_same_username())
        self.btn_join.clicked.connect(lambda state: self.join_access())
        self.btn_login.clicked.connect(lambda state: self.login_access())
        self.btn_send_message.clicked.connect(lambda state: self.send_msg_se())
        self.btn_transfer_file.clicked.connect(lambda state: self.send_file_to_chat_room())

    def initialize_app(self):
        self.btn_init.clicked.connect(lambda state: self.initialize_app())
        self.text_edit_chat_room.clear()
        self.text_edit_for_send_chat.clear()
        self.line_edit_for_join_id.clear()
        self.line_edit_for_join_pw.clear()
        self.line_edit_for_join_nick.clear()
        self.text_edit_chat_room.clear()
        self.valid_duplication_id = False
 # client function =================================
    # 클라 -> 서버 아이디 중복 체크 요청
    def assert_same_username(self):
        input_username = self.line_edit_for_join_id.text()
        self.client_app.send_join_id_for_assert_same_username(input_username)  # 헤더를 붙이고 보내는 동작(?)

    # 서버 -> 클라 아이디 중복 체크 결과 대응

    def assert_same_name_res(self, return_result: bool):

        if return_result is True:
            self.valid_duplication_id = True
            return QtWidgets.QMessageBox.about(self, "가능", "중복 없는 아이디, 써도됌")
        elif return_result is False:
            return QtWidgets.QMessageBox.about(self, "불가능", "중복 아이디, 새로 쓰기")

    # 클라 -> 서버 회원가입 요청
    def join_access(self):
        if self.valid_duplication_id is False:
            QtWidgets.QMessageBox.about(self, "어허", "아이디 중복확인 먼저 시행해주세요")
            return
        join_username = self.line_edit_for_join_id.text()
        join_pw = self.line_edit_for_join_pw.text()
        join_nickname = self.line_edit_for_join_nick.text()
        self.client_app.send_join_id_and_pw_for_join_access(join_username, join_pw, join_nickname)

    # 서버 -> 클라 회원가입 결과 체크 결과 대응
    def sign_up_res(self, return_result: bool):
        if return_result is True:
            return QtWidgets.QMessageBox.about(self, "성공", "회원가입 성공")
        elif return_result is False:
            return QtWidgets.QMessageBox.about(self, "실패", "회원가입 실패")

    #  클라 -> 서버 로그인 요청
    def login_access(self):
        login_username = self.line_edit_for_login_id.text()
        login_pw = self.line_edit_for_login_pw.text()
        self.client_app.send_login_id_and_pw_for_login_access(login_username, login_pw)

    # 서버 -> 클라 로그인 결과 체크 결과 대응
    def log_in_res(self, return_result: bool):
        if return_result is True:
            # 모든건 로그인 버튼을 누르면 시작한다. 나중에 수정
            self.enter_square()
            self.all_user_list()
            self.user_talk_room_list()
            self.talk_room_user_list_se()
            # self.out_talk_room()
            self.talk_room_msg()
            return QtWidgets.QMessageBox.about(self, "성공", "login 성공")
        elif return_result is False:
            return QtWidgets.QMessageBox.about(self, "실패", "login 실패")

    # 클라 -> 서버 초기 체팅방 입장, 로그인시 실행
    def enter_square(self):
        self.client_app.send_enter_square()

    # 서버 -> 클라 초기 체팅방 입장 결과 체크
    def enter_square_res(self):
        # 화면 띄우기? 화면전환?
        print("초기방 입장 완료")

    # 클라 -> 서버 유저 리스트 요청, 로그인시 할 수도있음
    def all_user_list(self):
        self.client_app.send_all_user_list()

    # 서버 -> 클라 유저 리스트 정보 받음
    def all_user_list_res(self, return_result: str):
        all_user_list = self.decoder.decode(return_result)
        print('가입한 유저 정보', all_user_list)

    # 클라 -> 서버 채팅방 리스트 요청
    def user_talk_room_list(self):
        self.client_app.send_user_talk_room_list()

    # 서버 -> 클라 채팅방 리스트 정보 받음
    def user_talk_room_list_res(self, return_result: str):
        talk_room_list = self.decoder.decode(return_result)
        print('존재하는 방 리스트', talk_room_list)

    # 클라 -> 서버 채팅방 관련 유저 정보 요청
    # 방 아이디를 넘겨줘야 할듯 하다.
    def talk_room_user_list_se(self):
        # self.client_app.send_talk_room_user_list_se(talk_room_id)
        pass

    # 서버 -> 클라 톡방 유저 객체 정보 획득
    def talk_room_user_list_se_res(self, talk_room_id:int, return_result: str):
        user_list = self.decoder.decode(return_result)

        print('방에 존재하는 유저 정보', user_list)

    # 클라 -> 서버 채팅방 나가기 요청
    # 방 아이디를 넘겨줘야 할듯 하다
    def out_talk_room(self):
        # self.client_app.send_out_talk_room(talk_room_id)
        pass

    # 채팅방 나가기 결과 반환
    # 메세지 박스를 화면 전환 해주세요
    def out_talk_room_res(self, return_result: bool):
        if return_result is True:
            return QtWidgets.QMessageBox.about(self, "성공", "방탈출 성공")
        elif return_result is False:
            return QtWidgets.QMessageBox.about(self, "실패", "방탈출 실패")
        # 화면 전환후 채팅방 목록 불러오기

    # 클라 -> 서버 메시지 전달
    def send_msg_se(self):
        txt_message = self.text_edit_for_send_chat.toPlainText()
        self.text_edit_for_send_chat.clear()
        self.text_edit_chat_room.appendPlainText(txt_message)
        self.client_app.send_send_msg_se(1, txt_message)

    # 서버 -> 클라 메시지 받기
    def send_msg_se_res(self, return_result: str):
        message = self.decoder.decode_any(return_result)
        self.text_edit_chat_room.appendPlainText(
            f"{message.user_obj.nickname} : {message.contents} > {message.send_time_stamp}")
        # todo: send 메시지

    # 클라 -> 서버 단톡방 초대 요청
    def invite_user_talk_room(self):
        # self.client_app.send_invite_user_talk_room(talk_room_id, invite_user)
        pass
    
    # 서버 -> 클라 단톡방 초대 완료
    def invite_user_talk_room_res(self, return_result: bool):
        print('초대완료')

    # 채팅방 개설하기
    def make_talk_room(self):
        # 시간은 어떻게 받을 지몰라서 그대로 둠. user_id도 같인 이유
        # self.client_app.send_make_talk_room(room_name, guest_list, open_time_stmp)
        pass

    def make_talk_room_res(self, return_result: bool):
        print('개설완료')
        # 단톡방 리스트 갱신하는 파일 만들기

    # 채팅방 입장시 클라 -> 서버 이전 message 내용 전송
    def talk_room_msg(self):
        self.client_app.send_talk_room_msg(talk_room_id=1)
    
    # 서버 -> 클라 message obj 내용 받기
    def talk_room_msg_res(self, return_result: str):
        room_msg = self.decoder.decode_any(return_result)
        # 오브젝트들 잘 나오는지 확인
        for i in room_msg:
            print(i)

    def send_file_to_chat_room(self):
        save_excel_dialog = QtWidgets.QMessageBox.question(self, "파일 업로드", "파일을 업로드합니까?")
        if save_excel_dialog == QtWidgets.QMessageBox.Yes:
            save_path_file_name, _, = QtWidgets.QFileDialog.getSaveFileName(self, '파일 저장', './')
            print(f"{save_path_file_name} send 로직 실행")
        # todo: send 메시지

 

금일 작업 내용:

- DB로 연결되어 동작되어있던 클라이언트 컨트롤러에서 떼어내어 Client 개체를 통해 동작하도록 구현하고 있음

- 오가는 데이터들이 많고, 다양한 자료형으로 구조되다보니 작업이 더딤

 


# 버그 수정한 json function

 def decode_obj(self, o, **kwargs):
        try:
            dict_obj = super().decode(o, **kwargs)
        except:
            dict_obj = o
        if 'user_talk_room_id' in dict_obj.keys():
            return UserTalkRoom(dict_obj['user_talk_room_id'], dict_obj['user_id'], dict_obj['talk_room_id'])
        elif 'user_id' in dict_obj.keys():
            return User(dict_obj['user_id'], dict_obj['username'], dict_obj['password'], dict_obj['nickname'])
        elif 'message_id' in dict_obj.keys():
            temp_user = User(*dict_obj['user_obj'].values())
            return Message(dict_obj['message_id'], dict_obj['sender_user_id'], dict_obj['talk_room_id'],
                           dict_obj['send_time_stamp'], dict_obj['contents'], dict_obj['long_contents_id'], temp_user)
        elif 'talk_room_id' in dict_obj.keys():
            return TalkRoom(dict_obj['talk_room_id'], dict_obj['talk_room_name'], dict_obj['open_time_stamp'])
        elif 'contents_id' in dict_obj.keys():
            return LongContents(dict_obj['contents_id'], dict_obj['contents_type'], dict_obj['long_text'],
                                dict_obj['image'])

 

해당 함수는 binary code된 json string을 dictionary로 구조화한 뒤 key를 통해 어떤 클래스로 반환할지 결정하는 핸들러이다.

다만 str으로 이미 decode된 경우엔 에러가 난다. try except 문을 통해 직접 parameter로 입력된 object가 binary상태인지, 이미 converting 된 상태인지 구별하도록 간단하게 처리했다.

 


FramelessWindow를 사용하다보면, 프로그램이 최상단에 띄워지면서 가려지게 된다. 그러면서 기존에 편리하게 사용하던 QMessagebox들이 가려져서 보이지 않게 되는 문제가 발생한다. 이를 해결하기 위해 QDialog를 상속받는 NoFrameMessageBox를 새로 선언하여 작성하였다.

class NoFrameMessageBox(QDialog, Ui_custom_message_box):
    def __init__(self, client_controller, title, contents, dialog_type):
        super().__init__()
        self.setupUi(self)
        self.client_controller = client_controller
        self.title = title
        self.contents = contents
        self.dialog_type = dialog_type
        self.result = None
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.WindowStaysOnTopHint)
        self.initial_setting()
        self.set_btn_trigger()
        self.exec()

    def mousePressEvent(self, event):
        self.client_controller.mousePressEvent(self, event)

    def mouseMoveEvent(self, event):
        self.client_controller.mouseMoveEvent(self, event)

    def initial_setting(self):
        self.label_title.setText(self.title)
        self.label_contents.setText(self.contents)

        if self.dialog_type == "about":
            self.btn_no.hide()

    def set_btn_trigger(self):
        self.btn_yes.clicked.connect(lambda state: self.answered(True))
        self.btn_no.clicked.connect(lambda state: self.answered(False))

    def answered(self, boolean_answer: bool):
        self.result = boolean_answer
        self.close()

 


채팅창에 들어갈 라벨들을 선언하는 클래스를 선언하였다. 스타일시트는 입력된 메시지에 따라 적용하도록 구현하였다.

class MessageLabel(QWidget, Ui_widget_message):
    CHAT_MESSAGE_STYLESHEET_OTHER = """
        #background_widget{
            background-color: #FFB07F;
            border-radius: 20px;
            border-style: solid;
            border-width: 2px;
            border-color: #FF52A2;
        }

        #label_nickname{
            font: bold 12pt "나눔고딕";	
            color: #F31559;
        }
        #label_message{
            font: bold 10pt "나눔고딕";	
            color: #001C30;
        }"""

    CHAT_MESSAGE_STYLESHEET_USER = """
        #background_widget{
            background-color: #A2FF86;
            border-radius: 20px;
            border-style: solid;
            border-width: 2px;
            border-color: #164B60;
        }

        #label_nickname{
            font: bold 12pt "나눔고딕";	
            color: #068FFF;
        }
        #label_message{
            font: bold 10pt "나눔고딕";	
            color: #2D4356;
        }"""

    def __init__(self, client_controller, message_obj:Message):
        super().__init__()
        self.setupUi(self)
        self.client_controller = client_controller
        self.message_obj = message_obj

        if isinstance(self.message_obj.user_obj, User):
            self.user_obj = self.message_obj.user_obj
        else:
            self.user_obj = self.client_controller.get_user_by_id(self.message_obj.sender_user_id)
        self.set_label()

    def set_label(self):
        self.label_message.setText(self.message_obj.contents)
        self.label_nickname.setText(self.user_obj.nickname)
        user = self.client_controller.get_user_self()
        if self.user_obj.user_id == user.user_id:
            self.spacer_right.hide()
            self.setStyleSheet(self.CHAT_MESSAGE_STYLESHEET_USER)
        else:
            self.spacer_left.hide()
            self.setStyleSheet(self.CHAT_MESSAGE_STYLESHEET_OTHER)
        timestamp = self.message_obj.send_time_stamp
        onset_time = get_subtract_time(timestamp)

 

 

내일까지 잘 마무리해볼 수 있도록 노력해보겠습니다.

 

감사합니다.

 

 

 

오늘 한 일 :

OO님 업무 이어 받아 UI 소스 코드 작성함. 파일을 각자 다르게 구분하다보니, 이미지 경로를 제대로 못불러오는 현상 발생

qrc(리소스)를 사용하여 인코딩 후 경로 설정하여 인코딩함

''' pyuic --import-from=Code.front.ui -x {ui}.ui -o ui_class_{ui}.py '''

명령어를 사용하면 된다.

 

그려면 compile 된 파일에서 저렇게 마지막 부분에 import 가 따라 붙어서 이미지를 참조할 수 있게된다.

1. pyuic

# ui_compiler.py
import os
import sys

if __name__ == '__main__':
    os.system(f"pyrcc5 ../src_img/my_qrc.qrc -o my_qrc_rc.py")

    uis = ['page_friend_list_ui', 'page_join_ui', 'page_login_ui', 'page_talk_room_list_ui', 'page_talk_room_ui',
           'widget_talk_room_invite_user_list', 'widget_search_talk_room_member_list',
           'widget_profile_item_ui', 'widget_chat_room_item_ui', 'profile_page_ui', 'widget_invite_user_item','widget_talk_room_invite_user_list_in_chat_room']
    for ui in uis:
        # os.system(f'python  -m PyQt5.uic.pyuic --from-imports -x {ui}.ui -o ui_class_{ui}.py')
        os.system(f'python  -m PyQt5.uic.pyuic --import-from=Code.front.ui -x {ui}.ui -o ui_class_{ui}.py')

 

pyrcc 모듈을 디자이너 사용할 때 초창기 때 쓰고 쓸 일이 없을 줄 알았는데, 이 방법만 안다면 경로 문제가 의외로 수월하게 풀릴 것 같다

 

 


2. json

class KKOEncoder(JSONEncoder):

    def __init__(self):
        super().__init__()


    def encode(self, o) -> str:
        if isinstance(o, list) and isinstance(o[0], User):
            temp_list = list()
            for obj in o:
                str_obj = obj.toJSON()
                temp_list.append(str_obj)
            return json.dumps(temp_list)

        elif isinstance(o, list) and isinstance(o[0], Message):
            temp_list = list()
            for obj in o:
                str_obj = obj.toJSON()
                temp_list.append(str_obj)
            return json.dumps(temp_list)

        return json.dumps(o, default=lambda o: o.__dict__)


    def default(self, o):
        return o.__dict__


class KKODecoder(JSONDecoder):
    def __init__(self):
        super().__init__()

    def decode_any(self, o, **kwargs):
        o: str
        if o.startswith("["):  # list type
            list_type_obj = json.loads(o)
            temp_list = list()
            for obj in list_type_obj:
                one_obj = self.decode_obj(obj)
                temp_list.append(one_obj)
            return temp_list

        else:
            return self.decode_obj(o, **kwargs)

    def decode_obj(self, o, **kwargs):

        dict_obj = super().decode(o, **kwargs)
        if 'user_talk_room_id' in dict_obj.keys():
            return UserTalkRoom(dict_obj['user_talk_room_id'], dict_obj['user_id'], dict_obj['talk_room_id'])
        elif 'user_id' in dict_obj.keys():
            return User(dict_obj['user_id'], dict_obj['username'], dict_obj['password'], dict_obj['nickname'])
        elif 'message_id' in dict_obj.keys():
            temp_user = User(*dict_obj['user_obj'].values())
            return Message(dict_obj['message_id'], dict_obj['sender_user_id'], dict_obj['talk_room_id'],
                           dict_obj['contents'], dict_obj['send_time_stamp'], dict_obj['long_contents_id'], temp_user)
        elif 'talk_room_id' in dict_obj.keys():
            return TalkRoom(dict_obj['talk_room_id'], dict_obj['talk_room_name'], dict_obj['open_time_stamp'])
        elif 'contents_id' in dict_obj.keys():
            return LongContents(dict_obj['contents_id'], dict_obj['contents_type'], dict_obj['long_text'],
                                dict_obj['image'])

 

list type의 json을 분리해준다.

3중 이상의 리스트는 구현하지 않도록 설계하여, 해당 로직으로 끝나는 걸 다행이라 생각한다.

 

 


 

내일은 정말 마무리단계이다. 오늘 꽤 많이 진행했는데, 잘 마무리 할 수 있으면 좋겠다.

 

감사합니다.

 

 

 

 

+ Recent posts