diff --git a/assets/questions_shuffled.txt b/assets/questions_shuffled.txt index e6e8080..d838b62 100644 --- a/assets/questions_shuffled.txt +++ b/assets/questions_shuffled.txt @@ -1,38 +1,38 @@ General|Which command creates a compressed tar archive?|bzip2 dir|gzip dir|tar czf archive.tar.gz dir|3 -General|How do you append output to a file in Bash?|>>|>|&>|1 -General|Which command finds all files named "passwd" under /etc?|find /etc -name passwd|ls /etc/passwd|grep passwd /etc|1 -General|How do you switch to the root user?|sudo root|sudo su -|su -|3 -General|Which command shows the SELinux context of a file?|ls -l|ls -Z|ls --context|2 -General|How do you create a new user?|useradd|usermod|adduser|1 -General|Which command lists all running containers?|docker ps|podman ps|podman list|2 -General|How do you mount an NFS share?|mount -t nfs server:/share /mnt|mount -o loop image.iso /mnt|mount -t ext4 /dev/sda1 /mnt|1 -General|How do you schedule a one-time job for 5 minutes from now?|at now + 5 minutes|cron 5|crontab -e|1 -General|Which command sets a file's permissions to rwxr-xr--?|chmod 777 file|chmod 754 file|chmod 644 file|2 -General|How do you check the status of firewalld?|firewall-cmd --state|systemctl firewall|firewallctl status|1 -General|How do you extend a logical volume by 1G?|lvresize -L 1G /dev/vg/lv|lvextend -L +1G /dev/vg/lv|lvcreate -L 1G /dev/vg/lv|2 -General|How do you display the last 10 lines of a file?|tail file|head file|less file|1 -General|How do you change a user's password aging policy?|usermod|passwd|chage|3 -General|How do you list all available systemd targets?|systemctl list-active-targets|systemctl list-units --type=target --all|systemctl list-targets|3 -General|How do you display the current runlevel?|runlevel|who -r|systemctl get-default|1 -General|How do you set a service to start at boot?|systemctl boot|systemctl start|systemctl enable|3 -General|How do you transfer a file securely to a remote host?|scp file user@host:/path|ftp file user@host:/path|rsync file user@host:/path|1 -General|How do you display the UUID of a filesystem?|blkid|lsblk|uuidgen|1 -General|How do you reload the systemd daemon?|systemctl reload|systemctl daemon-reload|systemctl restart|2 -Networking|How do you set the hostname to mars.domain250.example.com?|hostnamectl set-hostname mars.domain250.example.com|nmcli con mod hostname mars.domain250.example.com|echo "mars.domain250.example.com" > /etc/hostname|1 -Networking|Which command sets the IP address, gateway, and DNS for enp0s3?|ifconfig enp0s3 172.25.250.100 netmask 255.255.255.0|ip addr add 172.25.250.100/24 dev enp0s3|nmcli connection modify enp0s3 autoconnect yes ipv4.method manual ipv4.addresses 172.25.250.100 ipv4.gateway 172.25.250.254 ipv4.dns 172.25.250.254|3 +General|How do you append output to a file in Bash?|&>|>|>>|3 +General|Which command finds all files named "passwd" under /etc?|ls /etc/passwd|find /etc -name passwd|grep passwd /etc|2 +General|How do you switch to the root user?|sudo root|su -|sudo su -|2 +General|Which command shows the SELinux context of a file?|ls -Z|ls --context|ls -l|1 +General|How do you create a new user?|usermod|adduser|useradd|3 +General|Which command lists all running containers?|podman ps|podman list|docker ps|1 +General|How do you mount an NFS share?|mount -o loop image.iso /mnt|mount -t nfs server:/share /mnt|mount -t ext4 /dev/sda1 /mnt|2 +General|How do you schedule a one-time job for 5 minutes from now?|cron 5|crontab -e|at now + 5 minutes|3 +General|Which command sets a file's permissions to rwxr-xr--?|chmod 754 file|chmod 644 file|chmod 777 file|1 +General|How do you check the status of firewalld?|firewall-cmd --state|firewallctl status|systemctl firewall|1 +General|How do you extend a logical volume by 1G?|lvresize -L 1G /dev/vg/lv|lvcreate -L 1G /dev/vg/lv|lvextend -L +1G /dev/vg/lv|3 +General|How do you display the last 10 lines of a file?|head file|less file|tail file|3 +General|How do you change a user's password aging policy?|passwd|usermod|chage|3 +General|How do you list all available systemd targets?|systemctl list-units --type=target --all|systemctl list-active-targets|systemctl list-targets|3 +General|How do you display the current runlevel?|who -r|systemctl get-default|runlevel|3 +General|How do you set a service to start at boot?|systemctl boot|systemctl enable|systemctl start|2 +General|How do you transfer a file securely to a remote host?|rsync file user@host:/path|scp file user@host:/path|ftp file user@host:/path|2 +General|How do you display the UUID of a filesystem?|lsblk|uuidgen|blkid|3 +General|How do you reload the systemd daemon?|systemctl daemon-reload|systemctl restart|systemctl reload|1 +Networking|How do you set the hostname to mars.domain250.example.com?|nmcli con mod hostname mars.domain250.example.com|hostnamectl set-hostname mars.domain250.example.com|echo "mars.domain250.example.com" > /etc/hostname|2 +Networking|Which command sets the IP address, gateway, and DNS for enp0s3?|nmcli connection modify enp0s3 autoconnect yes ipv4.method manual ipv4.addresses 172.25.250.100 ipv4.gateway 172.25.250.254 ipv4.dns 172.25.250.254|ifconfig enp0s3 172.25.250.100 netmask 255.255.255.0|ip addr add 172.25.250.100/24 dev enp0s3|1 SELinux|How do you allow httpd to serve content on port 82?|semanage port -a -t http_port_t -p tcp 82|firewall-cmd --add-port=82/tcp --permanent|setenforce 0|1 User Management|Which command creates a user 'natasha' and adds to group 'sysmgrs'?|useradd natasha -G sysmgrs|useradd -g sysmgrs natasha|adduser natasha sysmgrs|1 User Management|How do you create a user 'sarah' with no interactive shell?|useradd sarah -G nologin|adduser sarah --no-login|useradd sarah --shell /sbin/nologin|3 -Scheduling|What is the correct crontab entry to log "EX200 in progress" every 2 min?|2 * * * * logger "EX200 in progress"|0 2 * * * logger "EX200 in progress"|*/2 * * * * logger "EX200 in progress"|3 -Permissions|How do you give user 'natasha' rw access to /var/tmp/fstab using ACL?|chmod 600 /var/tmp/fstab|chown natasha /var/tmp/fstab|setfacl -m u:natasha:rw- /var/tmp/fstab|3 +Scheduling|What is the correct crontab entry to log "EX200 in progress" every 2 min?|*/2 * * * * logger "EX200 in progress"|2 * * * * logger "EX200 in progress"|0 2 * * * logger "EX200 in progress"|1 +Permissions|How do you give user 'natasha' rw access to /var/tmp/fstab using ACL?|chown natasha /var/tmp/fstab|chmod 600 /var/tmp/fstab|setfacl -m u:natasha:rw- /var/tmp/fstab|3 Permissions|How do you deny user 'harry' all access to /var/tmp/fstab using ACL?|chmod 000 /var/tmp/fstab|setfacl -m u:harry:- /var/tmp/fstab|chown nobody /var/tmp/fstab|2 -Permissions|How do you set group ownership of /home/managers to sysmgrs and setgid?|chown root:sysmgrs /home/managers && chmod g+s /home/managers|setfacl -m g:sysmgrs:rwx /home/managers|chmod 770 /home/managers|1 -NTP|How do you configure chrony to use materials.example.com as NTP server?|systemctl enable ntpd|Edit /etc/chrony.conf and add 'server materials.example.com iburst'|ntpdate materials.example.com|2 -Autofs|How do you configure autofs to mount remoteuser1's home from materials.example.com?|autofs --mount /rhome/remoteuser1|Add '/rhome /etc/auto.rhome' to /etc/auto.master and 'remoteuser1 -rw materials.example.com:/rhome/remoteuser1' to /etc/auto.rhome|mount -t nfs materials.example.com:/rhome/remoteuser1 /rhome/remoteuser1|2 -User Management|How do you create user 'manalo' with UID 3533?|adduser manalo --uid=3533|useradd -u 3533 manalo|useradd manalo -U 3533|2 -Archiving|How do you create a gzip archive of /usr/local as /root/backup.tar.gz?|gzip /usr/local > /root/backup.tar.gz|tar -cvf /root/backup.tar.gz /usr/local|tar -czvf /root/backup.tar.gz /usr/local|3 -Swap|How do you add a 512M swap partition and make it persistent?|Create partition, mkswap /dev/vdb3, add UUID to /etc/fstab|swapon /dev/vdb3|echo "/dev/vdb3 swap swap defaults 0 0" >> /etc/fstab|1 -LVM|How do you create a logical volume 'qa' of 60 PE in group 'qagroup' and format ext3?|lvcreate -n qa -l 60 qagroup && mkfs.ext3 /dev/qagroup/qa|lvcreate -n qa -l 60 qagroup && mkfs.ext4 /dev/qagroup/qa|lvcreate -n qa -L 60M qagroup && mkfs.ext3 /dev/qagroup/qa|1 -Permissions|How do you give natasha rw and harry no access to /var/tmp/fstab using ACL?|chmod 600 /var/tmp/fstab|setfacl -m u:natasha:rw- /var/tmp/fstab|setfacl -m u:natasha:rw-,u:harry:- /var/tmp/fstab|3 +Permissions|How do you set group ownership of /home/managers to sysmgrs and setgid?|setfacl -m g:sysmgrs:rwx /home/managers|chown root:sysmgrs /home/managers && chmod g+s /home/managers|chmod 770 /home/managers|2 +NTP|How do you configure chrony to use materials.example.com as NTP server?|Edit /etc/chrony.conf and add 'server materials.example.com iburst'|ntpdate materials.example.com|systemctl enable ntpd|1 +Autofs|How do you configure autofs to mount remoteuser1's home from materials.example.com?|autofs --mount /rhome/remoteuser1|mount -t nfs materials.example.com:/rhome/remoteuser1 /rhome/remoteuser1|Add '/rhome /etc/auto.rhome' to /etc/auto.master and 'remoteuser1 -rw materials.example.com:/rhome/remoteuser1' to /etc/auto.rhome|3 +User Management|How do you create user 'manalo' with UID 3533?|useradd manalo -U 3533|adduser manalo --uid=3533|useradd -u 3533 manalo|3 +Archiving|How do you create a gzip archive of /usr/local as /root/backup.tar.gz?|tar -cvf /root/backup.tar.gz /usr/local|tar -czvf /root/backup.tar.gz /usr/local|gzip /usr/local > /root/backup.tar.gz|2 +Swap|How do you add a 512M swap partition and make it persistent?|swapon /dev/vdb3|echo "/dev/vdb3 swap swap defaults 0 0" >> /etc/fstab|Create partition, mkswap /dev/vdb3, add UUID to /etc/fstab|3 +LVM|How do you create a logical volume 'qa' of 60 PE in group 'qagroup' and format ext3?|lvcreate -n qa -l 60 qagroup && mkfs.ext4 /dev/qagroup/qa|lvcreate -n qa -l 60 qagroup && mkfs.ext3 /dev/qagroup/qa|lvcreate -n qa -L 60M qagroup && mkfs.ext3 /dev/qagroup/qa|2 +Permissions|How do you give natasha rw and harry no access to /var/tmp/fstab using ACL?|setfacl -m u:natasha:rw-,u:harry:- /var/tmp/fstab|setfacl -m u:natasha:rw- /var/tmp/fstab|chmod 600 /var/tmp/fstab|1 Scripting|How do you find all files under /usr less than 10M with sgid and save to /root/myfile?|find /usr -size -10M -perm -2000 > /root/myfile|find /usr -size -10M -perm -4000 > /root/myfile|find /usr -size +10M -perm -2000 > /root/myfile|1 -Scripting|How do you create a script to find SUID files between 30k and 50k in /usr?|find /usr -size +30k -size -50k -perm /g=s > /root/newfiles|find /usr -size +30k -size -50k -perm /o=s > /root/newfiles|find /usr -size +30k -size -50k -perm /u=s > /root/newfiles|3 +Scripting|How do you create a script to find SUID files between 30k and 50k in /usr?|find /usr -size +30k -size -50k -perm /g=s > /root/newfiles|find /usr -size +30k -size -50k -perm /u=s > /root/newfiles|find /usr -size +30k -size -50k -perm /o=s > /root/newfiles|2 diff --git a/assets/session.log b/assets/session.log index 2dde3ab..28f4879 100644 --- a/assets/session.log +++ b/assets/session.log @@ -1,3 +1,6 @@ -Session start: 2025-08-15 10:19:42 +Session start: 2025-08-15 11:16:11 +XP and level updated: 0 XP, Level 1 +XP and level updated: 0 XP, Level 1 +XP and level updated: 0 XP, Level 1 XP and level updated: 10 XP, Level 1 -Session end: 2025-08-15 10:19:52 +Session end: 2025-08-15 11:16:34 diff --git a/main.py b/main.py index 2f3796c..f506c42 100644 --- a/main.py +++ b/main.py @@ -279,7 +279,7 @@ def update_xp(correct): if correct: xp, level, leveled_up = stats.add_xp(10) else: - xp, level, _ = stats.get_xp_level() + xp, level = stats.get_xp_level() xp = max(0, xp - 5) stats.save_stats(xp, level, stats.get_total_time()) leveled_up = False @@ -290,6 +290,10 @@ def update_xp(correct): return xp, level def show_timer(minutes, questions_file, session_log, total_time, xp, level, root): + # Initialize timer variables BEFORE any inner function definitions + seconds_left = minutes * 60 + elapsed_time = 0 + def update_timer(): nonlocal seconds_left, elapsed_time if seconds_left > 0: @@ -354,27 +358,45 @@ def show_timer(minutes, questions_file, session_log, total_time, xp, level, root total_questions = 0 max_score = 300 - # Show all questions in random order + + # Shuffle and use each question only once per session random.shuffle(lines) - questions_to_ask = lines - question_idx = 0 + questions_to_ask = lines.copy() # Create a frame for question/answer widgets ONCE question_frame = tk.Frame(root) question_frame.pack(pady=10) + # Add a label to show per-category progress + progress_label = tk.Label(root, text="", font=("Helvetica", 10), fg="blue") + progress_label.pack(pady=2) + + def update_progress_label(): + # Always show progress for each category, even if empty + progress_items = [] + all_cats = set(score_by_category.keys()) + for q in lines: + cat = q.split('|')[0] if q.split('|')[0] else "General" + all_cats.add(cat) + for cat in sorted(all_cats): + correct, total = score_by_category.get(cat, [0, 0]) + progress_items.append(f"{cat[:3].upper()}:{correct}/{total}") + # Break into multiple lines if too many categories + max_per_line = 7 + lines_out = [" | ".join(progress_items[i:i+max_per_line]) for i in range(0, len(progress_items), max_per_line)] + progress_label.config(text="\n".join(lines_out)) + def ask_next_question(): - nonlocal question_idx, total_questions, total_correct - if question_idx >= len(questions_to_ask): + nonlocal total_questions, total_correct + if not questions_to_ask: # Show score page at end total_score = int((total_correct / total_questions) * max_score) if total_questions else 0 root.destroy() show_score_page(score_by_category, total_score) return - qline = questions_to_ask[question_idx] + qline = questions_to_ask.pop(0) parts = qline.split('|') if len(parts) < 5: - question_idx += 1 ask_next_question() return @@ -410,7 +432,7 @@ def show_timer(minutes, questions_file, session_log, total_time, xp, level, root radio_buttons.append(rb) def check_answer(): - nonlocal question_idx, total_questions, total_correct + nonlocal total_questions, total_correct sel = answer_var.get() correct = any(o['text'] == sel and o['correct'] for o in opts) # Find the correct answer @@ -429,20 +451,29 @@ def show_timer(minutes, questions_file, session_log, total_time, xp, level, root new_xp, new_level = update_xp(True) score_by_category.setdefault(category, [0, 0])[0] += 1 total_correct += 1 + score_by_category.setdefault(category, [0, 0])[1] += 1 + total_questions += 1 + for b in radio_buttons: b.config(state=tk.DISABLED) + xp_label.config(text=f"XP: {new_xp}") + level_label.config(text=f"Level: {new_level}") + update_progress_label() + root.after(500, ask_next_question) else: - messagebox.showinfo("Incorrect", f"Sorry, that's not correct.\n{detail}") new_xp, new_level = update_xp(False) - score_by_category.setdefault(category, [0, 0])[1] += 1 - total_questions += 1 - for b in radio_buttons: b.config(state=tk.DISABLED) - xp_label.config(text=f"XP: {new_xp}") - level_label.config(text=f"Level: {new_level}") - root.after(500, lambda: [ask_next_question()]) + score_by_category.setdefault(category, [0, 0])[1] += 1 + total_questions += 1 + xp_label.config(text=f"XP: {new_xp}") + level_label.config(text=f"Level: {new_level}") + update_progress_label() + # Show explanation, then immediately go to next question after dialog closes + def after_incorrect(): + ask_next_question() + messagebox.showinfo("Incorrect", f"Sorry, that's not correct.\n{detail}") + root.after(10, after_incorrect) submit_btn = tk.Button(question_frame, text="Submit", command=check_answer) submit_btn.pack(pady=5) - stats = UserStatsSqlite(DB_PATH, SUBSCRIPTION_END_DATE) timer_label = tk.Label(root, text=f"Time left: {minutes:02}:00:00", font=("Helvetica", 16)) timer_label.pack(pady=20) @@ -460,17 +491,22 @@ def show_timer(minutes, questions_file, session_log, total_time, xp, level, root total_time_label = tk.Label(root, text=f"Total session time: {format_time(stats.get_total_time())}", font=("Helvetica", 14)) total_time_label.pack(pady=5) - # Start the first question + def update_total_time_label(): + nonlocal elapsed_time + # Show total time as (previous total + elapsed this session) + current_total = stats.get_total_time() + elapsed_time + total_time_label.config(text=f"Total session time: {format_time(current_total)}") + root.after(1000, update_total_time_label) + + # Start the first question and live-updating total time + update_progress_label() + update_total_time_label() ask_next_question() def periodic_kill_steam(): kill_steam() root.after(10000, periodic_kill_steam) - seconds_left = minutes * 60 - elapsed_time = 0 - - root.after(1000, update_timer) root.after(1000, update_rol_timer) root.after(10000, periodic_kill_steam) diff --git a/rhel_learning.db b/rhel_learning.db index d65f0b3..1de9ae3 100644 Binary files a/rhel_learning.db and b/rhel_learning.db differ