├── .gitignore ├── README.md ├── attendance.py └── individual.py /.gitignore: -------------------------------------------------------------------------------- 1 | config.json 2 | .vscode 3 | debug.log 4 | chromedriver.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # auto-attendance 2 | Automated attendance recording tool for Moodle built using Selenium. 3 | 4 | ### For individuals 5 | 6 | The file [individual.py](https://github.com/aswinmprabhu/auto-attendance/blob/master/individual.py) contains the script for automatic attendance marking for one individual as shown in the above tutorial. All configuration is to be done within that single file. 7 | 8 | To run the script, 9 | 10 | 1. Clone the repository using the git command `git clone https://github.com/aswinmprabhu/auto-attendance.git` 11 | 2. Ensure that python3 and pip3 are installed 12 | 3. Download and place the chromedriver file in the repository folder 13 | 4. Edit the individual.py file 14 | * Modify the `moodle_mail` and `moodle_pass` variables with your credentials 15 | * Change the chromedriver file location and name if necessary (OS dependent) 16 | * Edit the `attendance_page_urls` variable with the URLs to all the attendance pages of your subjects 17 | 5. Inside the repository folder, execute the following commands 18 | ```bash 19 | pip install selenium 20 | python3 ./individual.py 21 | ``` 22 | (Try using pip3 is pip command does not work) 23 | 24 | ### For groups of individuals 25 | 26 | The file [attendance.py](https://github.com/aswinmprabhu/auto-attendance/blob/master/attendance.py) contains the script for automatic attendance recording for a group of individuals. A separate config file called `config.json` needs to be created for this to work. 27 | 28 | To run the script, 29 | 30 | 1. Clone the repository using the git command `git clone https://github.com/aswinmprabhu/auto-attendance.git` 31 | 2. Ensure that python3 and pip3 are installed 32 | 3. Download and place the chromedriver file in the repository folder 33 | 4. Create a config.json file inside the repository folder. A sample file is given below. 34 | ```javascript 35 | { 36 | "driver": "./chromedriver.exe", 37 | "interval": 20, 38 | "users": [ 39 | { 40 | "name": "user1", 41 | "email": "email1@mec.ac.in", 42 | "password": "pass1", 43 | "courses": [ 44 | "http://moodle.mec.ac.in/mod/attendance/view.php?id=9022", 45 | "http://moodle.mec.ac.in/mod/attendance/view.php?id=9002", 46 | ] 47 | }, 48 | { 49 | "name": "user2", 50 | "email": "email2@gmail.com", 51 | "password": "pass2", 52 | "courses": [ 53 | "http://moodle.mec.ac.in/mod/attendance/view.php?id=9027", 54 | "http://moodle.mec.ac.in/mod/attendance/view.php?id=9129", 55 | ] 56 | } 57 | ] 58 | } 59 | ``` 60 | 5. Make the necessary edits to config.json. Change the driver location if necessary and edit the user details. You can add more users by appending to the `users` array in the config. 61 | 6. Inside the repository folder, execute the following commands 62 | ```bash 63 | pip install selenium 64 | python3 ./attendance.py --config ./config.json 65 | ``` 66 | 67 | Please visit [MY BLOG](https://aswinmprabhu.netlify.app/posts/automating-attendance-recording-on-moodle-using-selenium/) for better understanding of this project. 68 | 69 | (Try using pip3 is pip command does not work) 70 | -------------------------------------------------------------------------------- /attendance.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.common.keys import Keys 3 | import time 4 | import os 5 | import sys 6 | import argparse 7 | import json 8 | 9 | 10 | def parse_config(config_file): 11 | config = {} 12 | with open(config_file) as f: 13 | config = json.load(f) 14 | if "interval" not in config: 15 | config["interval"] = 20 16 | if "driver" not in config: 17 | print('"driver" field not found in config') 18 | sys.exit(1) 19 | if "users" not in config: 20 | print('"users" field not found in config') 21 | sys.exit(1) 22 | 23 | return config 24 | 25 | 26 | def start_automation(users, driver): 27 | driver = webdriver.Chrome(driver) 28 | 29 | while 1: 30 | for user in users: 31 | driver.get("http://moodle.mec.ac.in") 32 | username_ip = driver.find_element_by_name("username") 33 | username_ip.clear() 34 | username_ip.send_keys(user["email"]) 35 | pass_ip = driver.find_element_by_name("password") 36 | pass_ip.clear() 37 | pass_ip.send_keys(user["password"]) 38 | pass_ip.send_keys(Keys.RETURN) 39 | 40 | if not driver.current_url == "http://moodle.mec.ac.in/my/": 41 | print("Failed to log in") 42 | sys.exit(1) 43 | 44 | for course in user["courses"]: 45 | driver.get(course) 46 | course_name_tag = driver.find_elements_by_xpath("//h1")[0] 47 | course_name = course_name_tag.text 48 | submit_attendance_btns = driver.find_elements_by_xpath( 49 | "//*[contains(text(), 'Submit attendance')]" 50 | ) 51 | if len(submit_attendance_btns) == 0: 52 | continue 53 | submit_attendance_btn = submit_attendance_btns[0] 54 | print("Marking " + user["name"] + "'s attendance for " + course_name) 55 | att_link = submit_attendance_btn.get_attribute("href") 56 | driver.get(att_link) 57 | status_btn = driver.find_elements_by_xpath("//input[@type='radio']")[0] 58 | status_btn.click() 59 | driver.find_element_by_id("id_submitbutton").click() 60 | print("Done") 61 | 62 | driver.get("http://moodle.mec.ac.in/login/logout.php") 63 | continue_btns = driver.find_elements_by_xpath( 64 | "//*[contains(text(), 'Continue')]" 65 | ) 66 | if len(continue_btns) == 0: 67 | continue 68 | continue_btn = continue_btns[0] 69 | continue_btn.click() 70 | time.sleep(10 * 60) 71 | 72 | 73 | def create_arg_parser(): 74 | # Creates and returns the ArgumentParser object 75 | 76 | parser = argparse.ArgumentParser(description="Automate your moodle attendance.") 77 | parser.add_argument("--config", help="Path to the JSON config file") 78 | return parser 79 | 80 | 81 | if __name__ == "__main__": 82 | arg_parser = create_arg_parser() 83 | parsed_args = arg_parser.parse_args(sys.argv[1:]) 84 | if not os.path.exists(parsed_args.config): 85 | print("Config file not found") 86 | sys.exit(1) 87 | 88 | config = parse_config(parsed_args.config) 89 | users = config["users"] 90 | driver = config["driver"] 91 | start_automation(users, driver) 92 | -------------------------------------------------------------------------------- /individual.py: -------------------------------------------------------------------------------- 1 | # Import webdriver module from selenium 2 | from selenium import webdriver 3 | 4 | # Import time module for waiting 20 mins 5 | import time 6 | 7 | # Create new driver object (change the file name based on your OS) 8 | driver = webdriver.Chrome("./chromedriver.exe") 9 | 10 | # Visit the moodle login page and fill in login details 11 | driver.get("http://moodle.mec.ac.in") 12 | 13 | # Define Moodle email and password 14 | moodle_mail = "" 15 | moodle_pass = "" 16 | 17 | # Find the field element and send the input keys to the element 18 | username_ip = driver.find_element_by_name("username") 19 | username_ip.send_keys(moodle_mail) 20 | pass_ip = driver.find_element_by_name("password") 21 | pass_ip.send_keys(moodle_pass) 22 | 23 | # Find the login button using XPATH and click it 24 | login_btn = driver.find_element_by_xpath('//*[@id="login"]/div[4]/input') 25 | login_btn.click() 26 | 27 | # Define the list of attendance page URLs 28 | attendance_page_urls = [ 29 | "http://moodle.mec.ac.in/mod/attendance/view.php?id=9022", 30 | "http://moodle.mec.ac.in/mod/attendance/view.php?id=9002", 31 | "http://moodle.mec.ac.in/mod/attendance/view.php?id=8961", 32 | ] 33 | 34 | # Start infinite loop 35 | while 1: 36 | # For every URL visit the page 37 | for url in attendance_page_urls: 38 | driver.get(url) 39 | 40 | # Get the list of all "Submit attendance" buttons on the page using XPATH 41 | submit_attendance_btns = driver.find_elements_by_xpath( 42 | "//*[contains(text(), 'Submit attendance')]" 43 | ) 44 | 45 | # If the list is empty, do nothing and continue 46 | if len(submit_attendance_btns) == 0: 47 | continue 48 | 49 | # Else visit the link (HTML "href" attribute) specified by the first button 50 | submit_attendance_btn = submit_attendance_btns[0] 51 | att_link = submit_attendance_btn.get_attribute("href") 52 | driver.get(att_link) 53 | 54 | # Find the fist attendance status radio button, ie, the one corresponding to "Present" 55 | status_btn = driver.find_elements_by_xpath("//input[@type='radio']")[0] 56 | 57 | # Click the status button and then the submit button 58 | status_btn.click() 59 | driver.find_element_by_id("id_submitbutton").click() 60 | 61 | # Sleep for 20 mins before trying again 62 | time.sleep(1 * 60) --------------------------------------------------------------------------------