티스토리 뷰

Python/PyQt5

cafe24 그룹웨어 일정 크롤링 알림이

j0n9m1n1 j0n9m1n1 2020. 2. 13. 16:52

#style관련 지워도 됨, 다른 그룹웨어 안들어가봐서 되는지 모름ㅎ

0. 실행 시 tray에 등록

    -트레이 우클릭 등록, 새로고침, 끝내기

1. selenium으로 gw login

    -bs로 로그인 못하겠음

2. driver cookie -> requests.Session에 넘겨 줌

3. Session으로 당일기준 범위 안의 일 별 일정 가져 옴(global day_range = 15)

4. DB로 넣어 줌

    - aws dynamo db사용(1. free tier무료 2. 간단 함)

    - 넣어주면서 key값으로 중복 검사 함, schedule_day_idx 중복 없었음

5. 없다면 새 일정으로 판단 > 윈도우 알림 줌(카톡으로 줘도 될 듯)

6. timer로 3600초에 한번씩 가져 옴

    -thread로 바꿔야 함

 

아이콘 저작권 확인도 안 함

 

import inspect
import json
import os
import platform
import sys
import threading
from datetime import datetime, timedelta
from pprint import pprint

import boto3
import requests
import xmltodict
from PyQt5.QtCore import (
    QCoreApplication,
    QDateTime,
    Qt,
    QThread,
    pyqtSignal,
    pyqtSlot,
)
from PyQt5.QtGui import QColor, QGuiApplication, QIcon, QPalette
from PyQt5.QtWidgets import (
    QAbstractItemView,
    QAction,
    QApplication,
    QBoxLayout,
    QCalendarWidget,
    QCheckBox,
    QComboBox,
    QDateEdit,
    QFrame,
    QGridLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QMainWindow,
    QMenu,
    QMessageBox,
    QPushButton,
    QRadioButton,
    QSizePolicy,
    QStatusBar,
    QStyle,
    QSystemTrayIcon,
    QTableWidget,
    QTableWidgetItem,
    QTabWidget,
    QTextEdit,
    QVBoxLayout,
    QWidget,
    qApp,
)
from selenium import webdriver


day_range = 15


class Main(QWidget):
    def __init__(self):
        super(Main, self).__init__()
        self.dow = ["월", "화", "수", "목", "금", "토", "일"]
        self.insert_cnt = 0
        self.data = {
            "schedule_day_idx": "",
            "schedule_day_type": "",
            "schedule_day_pidx": "",
            "schedule_day_name": "",
            "schedule_day_weekname": "",
            "schedule_day_stime": "",
            "schedule_day_etime": "",
            "schedule_day_date": "",
            "schedule_day_gubun": "",
            "schedule_day_memo": "",
            "schedule_day_cut_memo": "",
            "schedule_day_readcheck": "",
        }

        self.add_data = {
            "sm_member_id": "",
            "sm_pidx": "",
            "sm_gidx": "",
            "sm_pcount": "",
            "stype": "",
            "sm_write_type": "",
            "sm_gubun": "P",
            "sm_code": "",
            "sm_name": "",
            "sm_place": "",
            "sm_management": "",
            "sm_repeat": "",
            "sm_sdate": "",
            "sm_sadate": "",
            "sm_eadate": "",
            "sm_stime1": "",
            "sm_stime2": "",
            "sm_etime1": "",
            "sm_etime2": "",
            "sm_open": "",
            "sm_open_hidden": "",
            "sm_open_dept_hidden": "",
            "sm_goods": "",
            "sm_goods_hidden": "",
            "sm_inner": "",
            "sm_inner_hidden": "",
            "sm_memo": "",
            "sm_file": "(binary)",
            "sm_file_hidden": "",
            "sm_file_filehidden": "",
            "sp_date1": "",
            "sp_date2": "",
            "sm_repeat_gubun": "",
            "sp_stime1": "",
            "sp_stime2": "",
            "sp_etime1": "",
            "sp_etime2": "",
        }

        self.initUI()
        self.CheckSchedule()

    def initUI(self):

        self._translate = QCoreApplication.translate
        self.setWindowTitle("GW Schedule Manager")
        self.list_schedule_gubun = ["부서", "개인", "회사"]
        self.list_schedule_code = ["작업", "회의", "약속", "기타"]
        self.list_shour = ["{0:02d}".format(i) for i in range(0, 25)]
        self.list_ehour = ["{0:02d}".format(i) for i in range(0, 25)]
        self.list_smin = ["{0:02d}".format(i) for i in range(0, 51, 10)]
        self.list_emin = ["{0:02d}".format(i) for i in range(0, 51, 10)]

        self.icon = QIcon(r"C:\dev\python\Books\crawling2\calendar2.png")

        self.tray = QSystemTrayIcon(self)
        self.tray.setIcon(self.icon)
        self.tray.setVisible(True)

        self.date_schedule_dayrange = QDateEdit()
        self.date_schedule_dayrange.setCalendarPopup(True)
        self.date_schedule_dayrange.setDateTime(QDateTime.currentDateTime())
        self.text_schedule_memo = QTextEdit()
        self.text_schedule_memo.setHtml(
            self._translate(
                "Form",
                '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">\n'
                '<html><head><meta name="qrichtext" content="1" /><style type="text/css">\n'
                "p, li { white-space: pre-wrap; }\n"
                "</style></head><body style=\" font-family:'Gulim'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
                '<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">1</p>\n'
                '<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2</p>\n'
                '<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">3</p></body></html>',
            )
        )
        self.line_schedule_title = QLineEdit("제목: ")
        self.line_schedule_title.focusPolicy()
        self.line_schedule_joinlist = QLineEdit()
        self.line_schedule_joinlist.setEnabled(False)
        self.radio_post_gubun = QRadioButton("일정")
        self.radio_post_gubun.setChecked(True)

        self.combo_schedule_gubun = QComboBox()
        self.combo_schedule_gubun.addItems(self.list_schedule_gubun)
        self.combo_schedule_code = QComboBox()
        self.combo_schedule_code.addItems(self.list_schedule_code)
        self.combo_shour = QComboBox()
        self.combo_shour.addItems(self.list_shour)
        self.combo_shour.setCurrentText("09")
        self.combo_smin = QComboBox()
        self.combo_smin.addItems(self.list_smin)
        self.combo_smin.setCurrentText("00")
        self.combo_ehour = QComboBox()
        self.combo_ehour.addItems(self.list_ehour)
        self.combo_ehour.setCurrentText("18")
        self.combo_emin = QComboBox()
        self.combo_emin.addItems(self.list_emin)
        self.combo_emin.setCurrentText("00")

        self.chk_specific = QCheckBox("일정 상세등록")
        self.lab_post_gubun = QLabel("등록구분")
        self.lab_specific = QLabel("상세등록")
        self.lab_schedule_gubun = QLabel("일정구분")
        self.lab_schedule_code = QLabel("일정코드")
        self.lab_schedule_title = QLabel("일정명")
        self.lab_schedule_dayrange = QLabel("기간설정")
        self.lab_schedule_timerange = QLabel("시간")
        self.lab_schedule_joinlist = QLabel("내부참여자")
        self.lab_schedule_memo = QLabel("내용")

        self.btn_save = QPushButton("등록", clicked=self.ClickedSaveBtn)
        self.btn_cancel = QPushButton("취소", clicked=self.ClickedCancelBtn)
        self.btn_joinlist = QPushButton("선택")

        self.gbox = QGridLayout()
        self.hbox = QHBoxLayout()
        self.hbox1 = QHBoxLayout()
        self.hbox2 = QHBoxLayout()
        self.hbox3 = QHBoxLayout()

        self.hbox.addWidget(self.combo_shour)
        self.hbox.addWidget(self.combo_smin)
        self.hbox.addWidget(self.combo_ehour)
        self.hbox.addWidget(self.combo_emin)

        self.hbox1.addWidget(self.combo_schedule_code)
        self.hbox1.addWidget(self.lab_schedule_code)
        self.hbox1.addWidget(self.combo_schedule_gubun)

        self.hbox2.addWidget(self.line_schedule_joinlist)
        self.hbox2.addWidget(self.btn_joinlist)

        self.hbox3.addWidget(self.btn_save)
        self.hbox3.addWidget(self.btn_cancel)

        self.gbox.addWidget(self.lab_post_gubun, 0, 0, 1, 1)
        self.gbox.addWidget(self.radio_post_gubun, 0, 1, 1, 1)
        self.gbox.addWidget(self.lab_specific, 1, 0, 1, 1)
        self.gbox.addWidget(self.chk_specific, 1, 1, 1, 1)
        self.gbox.addWidget(self.lab_schedule_gubun, 2, 0, 1, 1)
        self.gbox.addWidget(self.lab_schedule_title, 3, 0, 1, 1)
        self.gbox.addWidget(self.line_schedule_title, 3, 1, 1, 1)
        self.gbox.addWidget(self.lab_schedule_dayrange, 4, 0, 1, 1)
        self.gbox.addWidget(self.date_schedule_dayrange, 4, 1, 1, 1)
        self.gbox.addWidget(self.lab_schedule_timerange, 5, 0, 1, 1)
        self.gbox.addWidget(self.lab_schedule_joinlist, 6, 0, 1, 1)
        self.gbox.addWidget(self.lab_schedule_memo, 7, 0, 1, 1)
        self.gbox.addWidget(self.text_schedule_memo, 7, 1, 1, 1)

        self.gbox.addLayout(self.hbox, 5, 1, 1, 1)
        self.gbox.addLayout(self.hbox1, 2, 1, 1, 1)
        self.gbox.addLayout(self.hbox2, 6, 1, 1, 1)
        self.gbox.addLayout(self.hbox3, 8, 1, 1, 1)

        self.setLayout(self.gbox)

        menu = QMenu()

        add_action = QAction("Add", self)
        refresh_action = QAction("Refresh", self)
        exit_action = QAction("Exit", self)

        add_action.triggered.connect(self.AddSchedule)
        refresh_action.triggered.connect(self.CheckSchedule)
        exit_action.triggered.connect(qApp.quit)

        menu.addAction(add_action)
        menu.addAction(refresh_action)
        menu.addAction(exit_action)

        self.tray.setContextMenu(menu)

    def ClickedSaveBtn(self):

        if self.combo_schedule_gubun.currentText() == "부서":
            self.add_data["sm_gubun"] = "T"
        elif self.combo_schedule_gubun.currentText() == "개인":
            self.add_data["sm_gubun"] = "P"
        if self.combo_schedule_code.currentText() == "작업":
            self.add_data["sm_code"] = "W"
        self.add_data["sm_name"] = self.line_schedule_title.text()
        self.add_data["sm_repeat"] = "N"
        self.add_data["sm_sdate"] = (
            self.date_schedule_dayrange.date()
        ).toString("yyyy-MM-dd")
        self.add_data["sm_stime1"] = self.combo_shour.currentText()
        self.add_data["sm_stime2"] = self.combo_smin.currentText()
        self.add_data["sm_etime1"] = self.combo_ehour.currentText()
        self.add_data["sm_etime2"] = self.combo_emin.currentText()
        self.add_data["sp_date1"] = (
            self.date_schedule_dayrange.date()
        ).toString("yyyy-MM-dd")
        self.add_data["sp_date2"] = (
            self.date_schedule_dayrange.date()
        ).toString("yyyy-MM-dd")
        self.add_data["sm_repeat_gubun"] = "A"
        self.add_data["sm_open"] = "지사?"
        self.add_data["sm_open_dept_hidden"] = "33"
        self.add_data["sm_memo"] = self.text_schedule_memo.toPlainText()
        self.add_data["sp_stime1"] = self.combo_shour.currentText()
        self.add_data["sp_stime2"] = self.combo_smin.currentText()
        self.add_data["sp_etime1"] = self.combo_ehour.currentText()
        self.add_data["sp_etime2"] = self.combo_emin.currentText()
        self.add_data["sm_write_type"] = "S"

        driver = self.InitDriver()
        driver = self.GWLogin(driver)
        cookies = driver.get_cookies()

        with requests.Session() as sess:
            for cookie in cookies:
                sess.cookies.set(cookie["name"], cookie["value"])
            sess.post(
                "http://gw.{yourdomain}.co.kr/groupware/schedule/schedule_write.php?mode=writeAct",
                data=self.add_data,
            )
        # pprint(self.add_data)
        self.tray.showMessage(
            "일정 등록", "일정 등록 완료", self.icon, 3000,  # Title  # Content
        )
        self.hide()

    def SetWidgets(self):
        self.line_schedule_title.setText("제목 ")
        self.date_schedule_dayrange.setDateTime(QDateTime.currentDateTime())
        self.text_schedule_memo.setHtml(
            self._translate(
                "Form",
                '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">\n'
                '<html><head><meta name="qrichtext" content="1" /><style type="text/css">\n'
                "p, li { white-space: pre-wrap; }\n"
                "</style></head><body style=\" font-family:'Gulim'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
                '<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">1:</p>\n'
                '<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2:</p>\n'
                '<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">3:</p></body></html>',
            )
        )

    def Notification(self, data):
        # 3 title
        # 5 stime
        # 6 etime
        # 7 date
        # 9 content
        # pprint(data)
        if data is not False:
            self.tray.showMessage(
                data[7]
                + " "
                + self.GetDoW(data[7])
                + " "
                + data[5]
                + "~"
                + data[6]
                + "\r\n"
                + data[3],
                data[9],
                self.icon,
                3000,  # Title  # Content
            )
        elif data is False:
            self.tray.showMessage(
                "일정 조회", "새로운 일정 없음", self.icon, 3000,  # Title  # Content
            )

    def InitDB(self):
        ddb = boto3.resource(
            service_name="dynamodb",
            region_name="{your_region}",
            aws_access_key_id="{key}",
            aws_secret_access_key="{key2}",
        )
        table = ddb.Table("schedule_data")

        return table

    def InitDriver(self):
        options = webdriver.ChromeOptions()
        options.add_argument("headless")
        options.add_argument("window-size=1920x1080")
        options.add_argument("disable-gpu")
        options.add_argument("--no-proxy-server")

        if platform.system() == "Windows":
            current_folder = os.path.realpath(
                os.path.abspath(
                    os.path.split(inspect.getfile(inspect.currentframe()))[0]
                )
            )
            driver_path = os.path.join(current_folder, "chromedriver.exe")
        elif platform.system() == "Linux":
            current_folder = os.path.dirname(os.path.realpath(__file__))
            driver_path = os.path.join(current_folder, "chromedriver")
        driver = webdriver.Chrome(driver_path, options=options)

        return driver

    def GWLogin(self, driver):
        driver.get("http://gw.pacs.co.kr")

        driver.find_element_by_xpath('//*[@id="gw_user_id"]').send_keys("{your_gw_id}")
        driver.find_element_by_xpath('//*[@id="gw_user_pw"]').send_keys(
            "{your_gw_pw}"
        )
        driver.find_element_by_xpath('//*[@id="loginBtn"]').click()
        return driver

    def GetDoW(self, date):
        year, month, day = date.split("-")
        r = datetime(int(year), int(month), int(day)).weekday()
        return self.dow[r]

    def InsertSchedule(self, table, data):
        try:
            reps = table.get_item(Key={"schedule_day_idx": data[0]})
            item = reps["Item"]
            print("overlap")
        except KeyError:
            for i, key in enumerate(self.data.keys()):
                self.data[key] = data[i]
            with table.batch_writer() as batch:
                batch.put_item(Item=self.data)
                self.Notification(data)
                self.insert_cnt += 1
            print("not overlap")

    def AddSchedule(self):
        self.SetWidgets()
        self.show()

    def ClickedCancelBtn(self):
        self.hide()

    def CheckSchedule(self):
        threading.Timer(3600, self.CheckSchedule).start()
        driver = self.InitDriver()

        driver = self.GWLogin(driver)
        cookies = driver.get_cookies()
        table = self.InitDB()
        with requests.Session() as sess:

            for cookie in cookies:
                sess.cookies.set(cookie["name"], cookie["value"])

            for r in range(-day_range, day_range):
                year, month, day = (
                    (datetime.now() + timedelta(days=r)).year,
                    (datetime.now() + timedelta(days=r)).month,
                    (datetime.now() + timedelta(days=r)).day,
                )
                html = sess.get(
                    f"http://gw.{your_domain}.co.kr/chtml/groupware/schedule.php?mode=dayAct&sdate={year}-{month}-{day}&sm_gubun=&sm_gubun_remarks=&my_schedule="
                )

                dumps = json.loads(json.dumps(xmltodict.parse(html.text)))
                try:
                    datas = list()
                    for cnt in range(len(dumps["result"]["schedule_daylist"])):

                        if 12 <= len(dumps["result"]["schedule_daylist"]):

                            for i, text in enumerate(
                                dumps["result"]["schedule_daylist"].values()
                            ):

                                if text is None:
                                    datas.append("")
                                else:
                                    datas.append(text)

                            self.InsertSchedule(table, datas)
                            break

                        elif 12 > len(dumps["result"]["schedule_daylist"]):
                            for i, text in enumerate(
                                dumps["result"]["schedule_daylist"][
                                    cnt
                                ].values()
                            ):

                                if text is None:
                                    datas.append("")
                                else:
                                    datas.append(text)

                                if (i + 1) % 12 == 0:
                                    self.InsertSchedule(table, datas)
                                    datas.clear()
                        else:
                            pass

                except KeyError:
                    pass
        if self.insert_cnt == 0:
            self.tray.showMessage(
                "일정 조회", "새로운 일정 없음", self.icon, 3000,  # Title  # Content
            )
        self.insert_cnt = 0
        driver.quit()


def SetPalette():
    darkPalette = QPalette()
    darkPalette.setColor(QPalette.WindowText, QColor(180, 180, 180))
    darkPalette.setColor(QPalette.Button, QColor(53, 53, 53))
    darkPalette.setColor(QPalette.Light, QColor(180, 180, 180))
    darkPalette.setColor(QPalette.Midlight, QColor(90, 90, 90))
    darkPalette.setColor(QPalette.Dark, QColor(35, 35, 35))
    darkPalette.setColor(QPalette.Text, QColor(180, 180, 180))
    darkPalette.setColor(QPalette.BrightText, QColor(180, 180, 180))
    darkPalette.setColor(QPalette.ButtonText, QColor(180, 180, 180))
    darkPalette.setColor(QPalette.Base, QColor(42, 42, 42))
    darkPalette.setColor(QPalette.Window, QColor(53, 53, 53))
    darkPalette.setColor(QPalette.Shadow, QColor(20, 20, 20))
    darkPalette.setColor(QPalette.Highlight, QColor(42, 130, 218))
    darkPalette.setColor(QPalette.HighlightedText, QColor(180, 180, 180))
    darkPalette.setColor(QPalette.Link, QColor(56, 252, 196))
    darkPalette.setColor(QPalette.AlternateBase, QColor(66, 66, 66))
    darkPalette.setColor(QPalette.ToolTipBase, QColor(53, 53, 53))
    darkPalette.setColor(QPalette.ToolTipText, QColor(180, 180, 180))
    darkPalette.setColor(QPalette.LinkVisited, QColor(80, 80, 80))

    return darkPalette


if __name__ == "__main__":

    app = QApplication(sys.argv)

    app.setStyle("Fusion")
    app.setPalette(SetPalette())

    with open(
        r"C:\dev\python\Books\crawling2\resources\style.qss"
    ) as stylesheet:
        app.setStyleSheet(stylesheet.read())
    ex = Main()

    sys.exit(app.exec_())