Skip to content

Commit

Permalink
first workin app
Browse files Browse the repository at this point in the history
  • Loading branch information
PaC-mEn committed Jul 16, 2022
0 parents commit 9267597
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Github token in order to let the code automaticly download Geco driver for windows.
GH_TOKEN=""

BASE_URL="https://live.timeclock365.com/login"
TIME_CLOCK365_USERNAME=""
TIME_CLOCK365_PASSWORD=""
SHIFT_START_TIME=""
SHIFT_END_TIME=""
HEADLESS=True # set to True if you wish firefox wan't open on your desktop when the script is called.
OPERATIONAL=True # Set to True only if you really wants to punch in and save your shifts.
3 changes: 3 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions .idea/auto-timeclock.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

200 changes: 200 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import os
import logging
import traceback
from time import sleep
from sys import platform
from time import strftime
from dotenv import load_dotenv
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.firefox import GeckoDriverManager
from selenium.webdriver.support import expected_conditions as EC


load_dotenv(".env")
BASE_URL = os.getenv("BASE_URL")
USERNAME = os.getenv("TIME_CLOCK365_USERNAME")
PASSWORD = os.getenv("TIME_CLOCK365_PASSWORD")
os.environ["GH_TOKEN"] = os.getenv("GH_TOKEN")
SHIFT_START_TIME = os.getenv("SHIFT_START_TIME")
SHIFT_END_TIME = os.getenv("SHIFT_END_TIME")
HEADLESS = True if os.getenv("HEADLESS") == 'True' else False
OPERATIONAL = True if os.getenv("OPERATIONAL") == 'True' else False


def reporter(exception):
pass


def init():
"""
Init a Selenium base on the OS and loads a web page using .env BASE_URL argument.
:return webdriver: initiated webdriver with Firefox page loaded with BASE_URL page.
:rtype: WebDriver
"""
logging.info("Initializing Browser")
try:
opt = Options()
opt.headless = HEADLESS

# Check running OS in order to start session correctly.
if "win32" in platform:
web_page = webdriver.Firefox(options=opt, executable_path=GeckoDriverManager().install())
else:
web_page = webdriver.Firefox(options=opt)
web_page.get(BASE_URL)

except Exception as e:
reporter("Failed to init FireFox client and get to the page.")
logging.info(f"Caught and exception while Initializing Browser \n {e}")
print(str(traceback.print_exc()) + "\nFailed to init FireFox client and get to the page.")
# web_page.close()

else:
logging.info("Finish Initializing Browser")
return web_page


def validate_field_write(element, field_content):
"""validating if field is filed with the right data, returned true if success.
:param element: WebDriver element that should be inspected.
:param field_content: The original content the operation trying to insert to field.
:type element: WebDriver
:type field_content: str
:rtype: bool
"""
logging.debug(f"""validating values element: {element.get_attribute("Value")} field_content: {field_content}""")
return element.get_attribute("Value") == field_content


def navigate_to_time_card(web_page):
"""
Gets already logged in live.timeclock365.com panel and only navigates to the time card page.
:param web_page: WebDriver - already logged-in Firefox page.
:return: None
"""
logging.info("Navigating to time card started.")
try:
WebDriverWait(web_page, 15).until(EC.presence_of_element_located((By.CLASS_NAME, "first"))).click()
timecard_elem = WebDriverWait(web_page, 15).until(EC.presence_of_all_elements_located((By.LINK_TEXT, "דיווח נוכחות")))[1]
timecard_elem.click()
except Exception as e:
logging.info(f"Caught and exception while Navigating to time card.\n {e}")
reporter("Field to navigate to timecard page")
print(str(e) + "\nField to navigate to timecard page")
# web_page.close()

logging.info("Finish Navigating to time card.")


def punch_in(web_page):
"""
Gets session on time card page finds punch in and out elements.
:param web_page: WebDriver - Time card Firefox page.
:type web_page: WebDriver
:rtype: None
"""
logging.info("Started to punch in time shifts.")
try:
# Finding the start and end shift fields.
punch_create_elem = web_page.find_elements(By.CLASS_NAME, "sonata-medium-date")
except Exception as e:
logging.info(f"Caught an exception while punch in time shifts. \n {e}")
reporter("Can't find start and end shift fields.")
print(str(e) + "start and end shift fields")
else:
# Filling shift start time
punch_create_elem[0].clear()
punch_create_elem[0].send_keys(strftime("%d.%m.20%y, ") + SHIFT_START_TIME)

# Filling shift end time
punch_create_elem[1].clear()
punch_create_elem[1].send_keys(strftime("%d.%m.20%y, ") + SHIFT_END_TIME)

# Click on the submit button to punch in if OPERATIONAL set to True.
if OPERATIONAL:
logging.info("Saving shifts by clicking on the save button.")
web_page.find_element(By.NAME, "btn_create_and_list").click()
logging.info("Finish Saving shifts.")


def login(web_page):
"""
Gets Firefox page loaded at BASE_URL and tries to log in to live.timeclock.com
using the Username and Password form .env file.
:param web_page: WebDriver - Firefox page loaded at BASE_URL.
:type web_page: WebDriver
:raise Exception: undefinable or unloaded elements.
:returns: None
:rtype: None
"""

# Handling possible exception when not finding username field.
logging.info("Start login to timecloick365.com")
try:
user_elem = web_page.find_element(By.NAME, "username")
except Exception as e:
logging.info(f"Caught and exception while log in \n {e}")
reporter("Failed to find username field, closing the script")
print(str(e) + "Failed to find username field, closing the script")
# web_page.close()

else:
for i in range(5):
if not validate_field_write(user_elem, USERNAME):
user_elem.send_keys(USERNAME)
else:
user_elem.send_keys(Keys.ENTER)
break
else:
logging.info(f"Caught an exception while login: unable to fill the username field.")
raise Exception("Error: Unable to fill username field.")

# Handling possible exception when finding password field, upon finding filing the password
try:
pass_elem = WebDriverWait(web_page, 10, poll_frequency=1).until(EC.presence_of_element_located((By.NAME, "password")))
sleep(0.5)
except Exception as e:
logging.info(f"Failed to find password field, closing the script, maybe invalid username \n {e}")
reporter(f"Failed to find password field, closing the script, maybe invalid username \n {e}")
print(str(e) + "Failed to find password field, closing the script, maybe invalid username")
# web_page.close()

else:
for i in range(5):
if not validate_field_write(pass_elem, PASSWORD):
pass_elem.send_keys(PASSWORD)
else:
web_page.find_element(By.CLASS_NAME, "login-page__submit").click()
break
else:
logging.info("Caught an exception while login: unable to fill the username field.")
raise Exception("Error: Unable to fill password field.")
logging.info("Finish login.")


def main():
logging.basicConfig(filename='./app.log', filemode="w",
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
logging.info('Starting Auto Punch In Script.')

web_page = init()
login(web_page)
navigate_to_time_card(web_page)
punch_in(web_page)
web_page.close()
logging.info("Task finished.")
print("Finish punch in.")


if __name__ == '__main__':
main()
23 changes: 23 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
async-generator==1.10
attrs==21.4.0
certifi==2022.6.15
cffi==1.15.0
charset-normalizer==2.0.12
cryptography==37.0.2
h11==0.13.0
idna==3.3
outcome==1.2.0
pycparser==2.21
pyOpenSSL==22.0.0
PySocks==1.7.1
python-dotenv==0.20.0
requests==2.28.0
selenium==4.2.0
sniffio==1.2.0
sortedcontainers==2.4.0
soupsieve==2.3.2.post1
trio==0.21.0
trio-websocket==0.9.2
urllib3==1.26.9
webdriver-manager==3.7.0
wsproto==1.1.0

0 comments on commit 9267597

Please sign in to comment.