From e0afd876b6b0b6959f394dbb693930400183067e Mon Sep 17 00:00:00 2001 From: kinoshitakenta Date: Tue, 28 May 2024 11:45:07 +0800 Subject: [PATCH] feature: basic features --- .gitignore | 11 ++++ TODO.txt | 2 + main.py | 96 +++++++++++++++++++++++++++++++ utils/EIP_action.py | 136 ++++++++++++++++++++++++++++++++++++++++++++ utils/cli.py | 9 +++ utils/driver.py | 16 ++++++ utils/utils.py | 77 +++++++++++++++++++++++++ 7 files changed, 347 insertions(+) create mode 100644 .gitignore create mode 100644 TODO.txt create mode 100644 main.py create mode 100644 utils/EIP_action.py create mode 100644 utils/cli.py create mode 100644 utils/driver.py create mode 100644 utils/utils.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..605c675 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# module cache +__pycache__/ + +# selenium driver +chromedriver.exe + +# log file +*.log + +# config file +config.toml diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..3efbf84 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,2 @@ +1. 過久沒登入,然後重登之後,要操作的頁面被關掉 +2. 重新一個新指令時,把其他開啟的頁面全部關掉 diff --git a/main.py b/main.py new file mode 100644 index 0000000..1a666de --- /dev/null +++ b/main.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +__author__ = 'kinoshitakenta' +__email__ = "ybs0306748@gmail.com" + +import logging +import os +from pathlib import Path + +from utils.cli import cli +from utils.driver import get_driver +from utils.EIP_action import ActionType, Action +from utils.utils import keep_login_status, LoginInfo + + +def get_usage() -> list[tuple]: + cmd_list = [] + for e in ActionType: + cmd_list.append((f"{e.value}", f"{e.name}")) + + cmd_list.append(("99", "重新輸入登入資訊")) + cmd_list.append(("exit", "關閉")) + + return cmd_list + + +def display_usage(): + if os.name == 'nt': + os.system('cls') + else: + os.system('clear') + + for cmd, msg in get_usage(): + print(f"{cmd}: {msg}") + + +def main(opt): + login_info = LoginInfo(Path(opt.config_path)) + + driver = get_driver() + keep_login_status(driver, login_info) + action_agent = Action(driver) + + display_usage() + + try: + while True: + cmd = input("\nInput action code: ").strip() + print(f"Retrieve cmd: {cmd}") + + # * command parse + if cmd == "exit": + print("Program is closing...") + break + elif cmd == "99": + login_info = LoginInfo(Path(opt.config_path)) + keep_login_status(driver, login_info) + continue + + if not cmd.isdigit(): + print("Input format error, please try again.") + continue + else: + action_code = int(cmd) + + if not ActionType.has_value(action_code): + print("Please enter an existing action code.") + continue + + # * run command + keep_login_status(driver, login_info) + action_agent.run(action_code) + + except KeyboardInterrupt: + pass + + driver.quit() + + +if __name__ == "__main__": + from rich.console import Console + from rich.traceback import install + + install(show_locals=True) + console = Console(tab_size=4, log_path=False) + + logging.basicConfig( + filename='log.log', + filemode='a+', + level=logging.DEBUG, + format='%(asctime)s %(levelname)-8s : %(message)s' + ) + + opt = cli() + main(opt) diff --git a/utils/EIP_action.py b/utils/EIP_action.py new file mode 100644 index 0000000..c53fb3d --- /dev/null +++ b/utils/EIP_action.py @@ -0,0 +1,136 @@ +import time +from enum import IntEnum + +from selenium import webdriver +from selenium.common.exceptions import NoSuchElementException +from selenium.common.exceptions import ElementClickInterceptedException +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import Select + + +class ActionType(IntEnum): + 登入 = 0 + 請假 = 1 + 補卡 = 2 + 訂便當 = 3 + + @classmethod + def has_value(cls, value): + return value in cls._value2member_map_ + + +class Action(): + def __init__(self, driver: webdriver.Chrome): + self.action_list = {ActionType.登入: self.__登入, + ActionType.請假: self.__請假, + ActionType.補卡: self.__補卡, + ActionType.訂便當: self.__訂便當 + } + self.driver = driver + + def run(self, action_type) -> None: + self.action_list[action_type]() + + def __登入(self): + pass + + def __請假(self): + """開啟請假單頁面""" + + # self.driver.find_element(By.ID, "ddlMenuTypes_DIV").click() + # time.sleep(0.05) + + # dropdown_element = self.driver.find_element(By.ID, "CopyOfComboBox") + # select = Select(dropdown_element) + # select.select_by_value("1") # 人力資源 + # time.sleep(0.05) + + # self.driver.find_element(By.ID, "ddlMenuItems1_DIV").click() + # time.sleep(0.05) + + # dropdown_element = self.driver.find_element(By.ID, "CopyOfComboBox") + # # * 因沒有更換值,所以無法收回選單 + # # // select = Select(dropdown_element) + # # // select.select_by_value("1_0") # 考勤管理作業 + # # * 以下列方式替代 + # dropdown_element.find_element( + # By.XPATH, ".//option[@value='1_0']").click() # 考勤管理作業 + # time.sleep(0.05) + + # menu = self.driver.find_element(By.ID, "BtnMenu") # 選單 + + # btn_root = self.driver.find_element( + # By.XPATH, ".//ul[@id='Stage1']") + # btn_list = btn_root.find_elements( + # By.XPATH, ".//li[@class='dropdown 1_0']") + + # btn = btn_list[0] # 請假單 + + # ActionChains(self.driver).move_to_element( + # menu).move_to_element(btn).click(btn).perform() + + self.driver.execute_script( + u"javascript:MenuItemClick('/MotorWeb/CHIPage/FuncTagPage.aspx?PageUrl=%7e%2fCHIHumrs%2fwHumrsExcuse.aspx&Form_FuncTag=CHIHumrs.Excuse', false, false, false)") + time.sleep(0.05) + + def __補卡(self): + """開啟刷卡補卡單頁面""" + + # self.driver.find_element(By.ID, "ddlMenuTypes_DIV").click() + # time.sleep(0.05) + + # dropdown_element = self.driver.find_element(By.ID, "CopyOfComboBox") + # select = Select(dropdown_element) + # select.select_by_value("1") # 人力資源 + # time.sleep(0.05) + + # self.driver.find_element(By.ID, "ddlMenuItems1_DIV").click() + # time.sleep(0.05) + + # dropdown_element = self.driver.find_element(By.ID, "CopyOfComboBox") + # dropdown_element.find_element( + # By.XPATH, ".//option[@value='1_0']").click() # 考勤管理作業 + # time.sleep(0.05) + + # menu = self.driver.find_element(By.ID, "BtnMenu") # 選單 + + # btn_root = self.driver.find_element( + # By.XPATH, ".//ul[@id='Stage1']") + # btn_list = btn_root.find_elements( + # By.XPATH, ".//li[@class='dropdown 1_0']") + + # btn = btn_list[2] # 刷卡補卡單 + + # ActionChains(self.driver).move_to_element( + # menu).move_to_element(btn).click(btn).perform() + + self.driver.execute_script( + u"javascript:MenuItemClick('/MotorWeb/CHIPage/FuncTagPage.aspx?PageUrl=%7e%2fCHIHumrs%2fwHumrsAppendClk.aspx&Form_FuncTag=CHIHumrs.AppendClk', false, false, false)") + time.sleep(0.05) + + def __訂便當(self): + """開啟所有未訂購的便當訂購頁面""" + + self.driver.switch_to.frame(self.driver.find_element(By.ID, "main")) + all_orders = self.driver.find_elements( + By.XPATH, ".//table[@id='WPMyOrder_tbMyOrder']/tbody/tr") + + open_page_num = 0 + for order in all_orders: + a_tag = order.find_elements( + By.XPATH, ".//td[@valign='baseline']/a") + assert len(a_tag) == 3 + + order_title, img, order_status = a_tag + if order_status.text != "【已訂購】": + order_title.click() + open_page_num += 1 + time.sleep(0.05) + + if open_page_num > 0: + print(f"已開啟 {open_page_num} 個訂購頁面") + else: + print("沒有尚未訂購的團購訂單") + + self.driver.switch_to.default_content() diff --git a/utils/cli.py b/utils/cli.py new file mode 100644 index 0000000..8b3022e --- /dev/null +++ b/utils/cli.py @@ -0,0 +1,9 @@ +import argparse + + +def cli(): + parser = argparse.ArgumentParser() + parser.add_argument("--config_path", type=str, default="config.toml", + help="Specify config file path.") + + return parser.parse_args() diff --git a/utils/driver.py b/utils/driver.py new file mode 100644 index 0000000..d5b3d62 --- /dev/null +++ b/utils/driver.py @@ -0,0 +1,16 @@ +from selenium import webdriver +from selenium.webdriver.chrome.service import Service +from selenium.webdriver.chrome.webdriver import WebDriver + + +def get_driver() -> WebDriver: + service = Service(executable_path='./chromedriver.exe') + + options = webdriver.ChromeOptions() + # options.add_argument("--headless") + options.add_argument('--disable-gpu') + options.add_experimental_option('excludeSwitches', ['enable-logging']) + driver = webdriver.Chrome(service=service, options=options) + driver.implicitly_wait(4) + + return driver diff --git a/utils/utils.py b/utils/utils.py new file mode 100644 index 0000000..3907e7f --- /dev/null +++ b/utils/utils.py @@ -0,0 +1,77 @@ +import getpass +import time +from pathlib import WindowsPath + +import tomli +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import Select + + +class LoginInfo(): + def __init__(self, config_path: WindowsPath): + with open(config_path, mode="rb") as f: + config = tomli.load(f) + + self.lang = config["login_info"]["lang"] + self.login_ID = config["login_info"]["login_ID"] + self.login_passwd = config["login_info"]["login_passwd"] + self.company_ID = config["login_info"]["company_ID"] + + if self.login_ID == "": + self.login_ID = getpass.getpass( + "please enter your ID: ").strip() + + if self.login_passwd == "": + self.login_passwd = getpass.getpass( + "please enter your passwd: ") + + +def keep_login_status(driver: webdriver.Chrome, login_info: LoginInfo): + EIP_url = "https://eip.techmation.com.tw/MotorWeb/MotorFaceDefaultNew.aspx" + + # * Close all windows except the main window. + while len(driver.window_handles) > 1: + driver.switch_to.window(driver.window_handles[1]) + time.sleep(0.05) + driver.close() + driver.switch_to.window(driver.window_handles[0]) + + top_page = driver.current_window_handle + driver.get(EIP_url) + time.sleep(0.3) + + if driver.title == "CHI MOTOR WEB ERP 登入": + # * Fill in all login information. + dropdown_element = driver.find_element(By.ID, "ddlLang") + select = Select(dropdown_element) + select.select_by_value(login_info.lang) + + input_text_element = driver.find_element(By.ID, "txtLoginID") + input_text_element.clear() + input_text_element.send_keys(login_info.login_ID) + + input_text_element = driver.find_element(By.ID, "txtLoginPwd") + input_text_element.clear() + input_text_element.send_keys(login_info.login_passwd) + + dropdown_element = driver.find_element(By.ID, "ddlCompanyID") + select = Select(dropdown_element) + select.select_by_value(login_info.company_ID) + + # * Press the submit button. + submit_btn = driver.find_element(By.ID, "btnLogin") + submit_btn.click() + + time.sleep(3) + + # * If login has pop a new window, switch main window to the new one. + for handle in driver.window_handles: + if handle != top_page: + new_page = handle + + driver.switch_to.window(top_page) + driver.close() + + driver.switch_to.window(new_page) + driver.maximize_window()