Forum Discussion
Rise 360 - Mass upload of questions into a question bank
Hi Paul. I literally just used AI to create a python script to turn LearnDash wpProQuiz XML exports into CSV files I could convert to XLS files because I was mistaken that Rise could do the XLS imports. It's a perfect conversion that works in seconds and is now pretty much useless without that last step.
Would you be able to video demo your process on a dummy file and share with the community?
I'm happy to leave an example of the excel file and share the proof of concept code here.
- Excel columns with green text are used in the script.
- This automation is for creating questions with feedback by answer choice.
NB: Mentioning again, if the UI changes, this assistive code will not function. On retrying today it did not work, I assume because devs introduced 'Add Audio', thus increasing the tabs needed to navigate.(It has been updated to account for the change)
This tab-order route is brittle, far better options using selectors but even those can change.
import time
from pathlib import Path
from openpyxl import load_workbook
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# -----------------------
# CONFIG
# -----------------------
EXCEL_PATH = Path(r"C:\Users\USER\ExcelQuestionFile.xlsx") # <- change
SHEET_NAME = None # None = active sheet, or set a name like "Sheet1"
START_ROW = 2 # row 2 of Excel file
# columns referenced:
# B=question title, C=?, D=?, E=?, F=?, H=?, I=?, J=?, K=?
COL = {
"B": "question_title",
"C": "col_c",
"D": "col_d",
"E": "col_e",
"F": "col_f",
"H": "col_h",
"I": "col_i",
"J": "col_j",
"K": "col_k",
}
# Timeouts / pacing
WAIT_SECS = 20
PAUSE = 0.25
# -----------------------
# HELPERS
# -----------------------
def wait_click(driver, css):
el = WebDriverWait(driver, WAIT_SECS).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, css))
)
el.click()
time.sleep(PAUSE)
return el
def wait_present(driver, css):
el = WebDriverWait(driver, WAIT_SECS).until(
EC.presence_of_element_located((By.CSS_SELECTOR, css))
)
return el
def tab_n(driver, n):
active = driver.switch_to.active_element
for _ in range(n):
active.send_keys(Keys.TAB)
time.sleep(0.05)
active = driver.switch_to.active_element
def clear_active_field(driver):
active = driver.switch_to.active_element
active.send_keys(Keys.CONTROL, "a")
active.send_keys(Keys.BACKSPACE)
time.sleep(PAUSE)
def type_into_active(driver, text):
active = driver.switch_to.active_element
# if cell is empty/None, type nothing
active.send_keys("" if text is None else str(text))
time.sleep(PAUSE)
def triple_click_select_all(driver):
active = driver.switch_to.active_element
actions = ActionChains(driver)
actions.click(active).pause(0.05).click(active).pause(0.05).click(active).perform()
time.sleep(0.1)
# -----------------------
# MAIN
# -----------------------
def main():
if not EXCEL_PATH.exists():
raise FileNotFoundError(f"Excel file not found: {EXCEL_PATH}")
wb = load_workbook(EXCEL_PATH)
ws = wb[SHEET_NAME] if SHEET_NAME else wb.active
# Start Chrome (visible)
options = webdriver.ChromeOptions()
options.add_argument("--start-maximized")
driver = webdriver.Chrome(options=options)
wait = WebDriverWait(driver, WAIT_SECS)
print("\nOpen Rise in this Chrome window.")
print("1) Log in manually")
print("2) Navigate manually to the Question Bank screen")
input("\nWhen you're on the Question Bank page, press ENTER here to start...")
row = START_ROW
while True:
# Read row data
b = ws[f"B{row}"].value
c = ws[f"C{row}"].value
d = ws[f"D{row}"].value
e = ws[f"E{row}"].value
f = ws[f"F{row}"].value
h = ws[f"H{row}"].value
i = ws[f"I{row}"].value
j = ws[f"J{row}"].value
k = ws[f"K{row}"].value
# Stop condition: if B is blank, assume we're done
if b is None or str(b).strip() == "":
print(f"\nRow {row}: Column B empty. Stopping.")
break
print(f"\nRow {row}: creating question: {b}")
# 1) Click "Add Question"
wait_click(driver, "button.sidebar-list__add-question-button")
# 2) Click "Multiple choice" menu item
# You provided: button[data-menu-item][data-name="MULTIPLE_CHOICE"]
wait_click(driver, 'button[data-menu-item][data-name="MULTIPLE_CHOICE"]')
# 3) Click "Any Response" trigger button
any_response_btn = wait.until(EC.element_to_be_clickable(
(By.XPATH, '//button[contains(@class,"menu__trigger")][.//span[contains(normalize-space(.),"Any Response")]]')
))
any_response_btn.click()
time.sleep(PAUSE)
# 4) Select "By Choice"
by_choice_li = wait.until(EC.element_to_be_clickable(
(By.XPATH, '//li[contains(@class,"menu__item")][.//span[normalize-space(.)="By Choice"]]')
))
by_choice_li.click()
time.sleep(PAUSE)
# 5) Focus question title
title_div = wait.until(
EC.element_to_be_clickable(
(
By.XPATH,
'//*[@contenteditable="true" and not(ancestor::li)]'
)
)
)
title_div.click()
time.sleep(0.2)
# Clear existing text
clear_active_field(driver)
# 6) Paste Column B (question title)
title_div.send_keys("" if b is None else str(b))
time.sleep(PAUSE)
# 7) Tab 5, delete 8
tab_n(driver, 5)
clear_active_field(driver)
# 8) Paste C, tab
type_into_active(driver, c)
tab_n(driver, 1)
# 9) Paste H, tab 2
type_into_active(driver, h)
tab_n(driver, 2)
# 10) delete 8, paste D, tab
clear_active_field(driver)
type_into_active(driver, d)
tab_n(driver, 1)
# 11) Paste I, tab 2
type_into_active(driver, i)
tab_n(driver, 2)
# 12) Paste E, tab
type_into_active(driver, e)
tab_n(driver, 1)
# 13) Paste J, tab 2
type_into_active(driver, j)
tab_n(driver, 3)
# 14) Paste F, tab
type_into_active(driver, f)
tab_n(driver, 1)
# 15) Paste K
type_into_active(driver, k)
# Optional: small pause to watch it
time.sleep(0.5)
row += 1
print("\nDone. Leaving browser open.")
input("Press ENTER to close browser...")
driver.quit()
if __name__ == "__main__":
main()
Related Content
- 1 year ago