Implement migration script from text files to SQLite database

- Added `migrate_to_sqlite.py` to handle migration of user stats and session logs from text files to an SQLite database.
- The script reads XP, level, and total time from respective text files and saves them into the `user_stats` table in the SQLite database.
- Session logs are parsed and logged into the `session_log` table.
- Existing text files are deleted after successful migration.
- Created a new SQLite database file `rhel_learning.db` with tables for questions, user stats, and session logs.
This commit is contained in:
2025-08-15 10:20:02 +02:00
parent 44062c6d37
commit c68baecb52
11 changed files with 534 additions and 752 deletions

Binary file not shown.

View File

@@ -1 +1 @@
3
1

View File

@@ -17,4 +17,23 @@ How do you display the current runlevel?|runlevel|who -r|systemctl get-default|1
How do you set a service to start at boot?|systemctl enable|systemctl start|systemctl boot|1
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
How do you display the UUID of a filesystem?|blkid|lsblk|uuidgen|1
How do you reload the systemd daemon?|systemctl daemon-reload|systemctl reload|systemctl restart|1
How do you reload the systemd daemon?|systemctl daemon-reload|systemctl reload|systemctl restart|1
Networking|How do you set the hostname to mars.domain250.example.com?|hostnamectl set-hostname mars.domain250.example.com|echo "mars.domain250.example.com" > /etc/hostname|nmcli con mod hostname mars.domain250.example.com|1
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|adduser natasha sysmgrs|useradd -g sysmgrs natasha|1
User Management|How do you create a user 'sarah' with no interactive shell?|useradd sarah --shell /sbin/nologin|useradd sarah -G nologin|adduser sarah --no-login|1
User Management|How do you set the password for user 'natasha' to 'password'?|echo "password" | passwd --stdin natasha|passwd natasha password|usermod -p password natasha|1
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"|1
Permissions|How do you give user 'natasha' rw access to /var/tmp/fstab using ACL?|setfacl -m u:natasha:rw- /var/tmp/fstab|chmod 600 /var/tmp/fstab|chown natasha /var/tmp/fstab|1
Permissions|How do you deny user 'harry' all access to /var/tmp/fstab using ACL?|setfacl -m u:harry:- /var/tmp/fstab|chmod 000 /var/tmp/fstab|chown nobody /var/tmp/fstab|1
Permissions|How do you set group ownership of /home/managers to sysmgrs and setgid?|chown root:sysmgrs /home/managers && chmod g+s /home/managers|chmod 770 /home/managers|setfacl -m g:sysmgrs:rwx /home/managers|1
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'|systemctl enable ntpd|ntpdate materials.example.com|1
Autofs|How do you configure autofs to mount remoteuser1's home from materials.example.com?|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|autofs --mount /rhome/remoteuser1|1
User Management|How do you create user 'manalo' with UID 3533?|useradd -u 3533 manalo|adduser manalo --uid=3533|useradd manalo -U 3533|1
Archiving|How do you create a gzip archive of /usr/local as /root/backup.tar.gz?|tar -czvf /root/backup.tar.gz /usr/local|gzip /usr/local > /root/backup.tar.gz|tar -cvf /root/backup.tar.gz /usr/local|1
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 60M qagroup && mkfs.ext3 /dev/qagroup/qa|lvcreate -n qa -l 60 qagroup && mkfs.ext4 /dev/qagroup/qa|1
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|chmod 600 /var/tmp/fstab|setfacl -m u:natasha:rw- /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 -2000 > /root/myfile|find /usr -size -10M -perm -4000 > /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 /u=s > /root/newfiles|find /usr -size +30k -size -50k -perm /g=s > /root/newfiles|find /usr -size +30k -size -50k -perm /o=s > /root/newfiles|1

View File

@@ -1,20 +1,38 @@
Which command creates a compressed tar archive?|gzip dir|tar czf archive.tar.gz dir|bzip2 dir|2
How do you append output to a file in Bash?|>>|>|&>|1
Which command finds all files named "passwd" under /etc?|find /etc -name passwd|ls /etc/passwd|grep passwd /etc|1
How do you switch to the root user?|sudo root|sudo su -|su -|3
Which command shows the SELinux context of a file?|ls --context|ls -Z|ls -l|2
How do you create a new user?|usermod|adduser|useradd|3
Which command lists all running containers?|docker ps|podman ps|podman list|2
How do you mount an NFS share?|mount -t ext4 /dev/sda1 /mnt|mount -t nfs server:/share /mnt|mount -o loop image.iso /mnt|2
How do you schedule a one-time job for 5 minutes from now?|at now + 5 minutes|cron 5|crontab -e|1
Which command sets a file's permissions to rwxr-xr--?|chmod 644 file|chmod 754 file|chmod 777 file|2
How do you check the status of firewalld?|systemctl firewall|firewall-cmd --state|firewallctl status|2
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
How do you display the last 10 lines of a file?|less file|tail file|head file|2
How do you change a user's password aging policy?|usermod|chage|passwd|2
How do you list all available systemd targets?|systemctl list-units --type=target --all|systemctl list-active-targets|systemctl list-targets|3
How do you display the current runlevel?|runlevel|who -r|systemctl get-default|1
How do you set a service to start at boot?|systemctl start|systemctl boot|systemctl enable|3
How do you transfer a file securely to a remote host?|scp file user@host:/path|rsync file user@host:/path|ftp file user@host:/path|1
How do you display the UUID of a filesystem?|uuidgen|blkid|lsblk|2
How do you reload the systemd daemon?|systemctl reload|systemctl restart|systemctl daemon-reload|3
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
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
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
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

View File

@@ -1,458 +1,3 @@
Session start: 2025-01-07 21:54:54
Session start: 2025-01-07 21:57:48
Session start: 2025-08-15 10:19:42
XP and level updated: 10 XP, Level 1
Session end: 2025-01-07 21:58:15
Session start: 2025-01-07 21:59:58
XP and level updated: 20 XP, Level 1
Session end: 2025-01-07 22:00:04
Session start: 2025-01-07 22:01:15
XP and level updated: 30 XP, Level 1
Session end: 2025-01-07 22:01:23
Session start: 2025-01-07 22:02:38
XP and level updated: 40 XP, Level 1
Session end: 2025-01-07 22:03:05
Session start: 2025-01-07 22:05:06
XP and level updated: 35 XP, Level 1
XP and level updated: 30 XP, Level 1
XP and level updated: 40 XP, Level 1
Session end: 2025-01-07 22:05:32
Session start: 2025-01-07 22:07:04
XP and level updated: 50 XP, Level 1
Session end: 2025-01-07 22:07:33
Session start: 2025-01-07 23:37:33
XP and level updated: 45 XP, Level 1
XP and level updated: 55 XP, Level 1
Session end: 2025-01-07 23:40:16
Session start: 2025-01-07 23:40:32
XP and level updated: 65 XP, Level 1
Session end: 2025-01-07 23:40:48
Session start: 2025-01-07 23:42:08
XP and level updated: 75 XP, Level 1
Session end: 2025-01-07 23:42:41
Session start: 2025-01-07 23:44:30
XP and level updated: 85 XP, Level 1
Session start: 2025-01-07 23:46:25
XP and level updated: 95 XP, Level 1
Session end: 2025-01-07 23:46:46
Session start: 2025-01-07 23:49:54
XP and level updated: 5 XP, Level 2
Session end: 2025-01-07 23:51:02
Session start: 2025-01-07 23:53:24
XP and level updated: 0 XP, Level 2
Session end: 2025-01-07 23:53:50
Session start: 2025-01-07 23:56:05
XP and level updated: 0 XP, Level 2
Session end: 2025-01-07 23:56:40
Session start: 2025-01-07 23:58:57
Session start: 2025-01-08 00:00:35
Session start: 2025-01-08 00:04:38
XP and level updated: 0 XP, Level 2
Session end: 2025-01-08 00:04:59
Session start: 2025-01-08 00:11:09
XP and level updated: 10 XP, Level 2
Session end: 2025-01-08 00:11:34
Session start: 2025-01-08 00:14:22
XP and level updated: 5 XP, Level 2
Session end: 2025-01-08 00:14:36
Session start: 2025-01-08 19:45:55
Session start: 2025-01-08 19:45:56
Session start: 2025-01-08 19:45:56
Session start: 2025-01-08 19:45:56
Session start: 2025-01-08 19:45:57
Session start: 2025-01-08 19:51:08
XP and level updated: 15 XP, Level 2
Session end: 2025-01-08 20:11:28
Session start: 2025-01-08 20:55:07
XP and level updated: 25 XP, Level 2
Session end: 2025-01-08 20:55:36
Session start: 2025-01-08 21:08:29
Session start: 2025-01-08 21:08:29
Session start: 2025-01-08 21:08:29
Session start: 2025-01-08 21:08:30
Session start: 2025-01-08 21:08:30
Session start: 2025-01-08 21:19:58
Session start: 2025-01-08 21:19:58
Session start: 2025-01-08 21:19:58
Session start: 2025-01-08 21:19:58
Session start: 2025-01-08 21:19:59
Session start: 2025-01-08 23:03:13
Session start: 2025-01-08 23:03:14
Session start: 2025-01-08 23:03:14
Session start: 2025-01-08 23:03:15
Session start: 2025-01-08 23:03:15
Session start: 2025-01-09 19:32:07
Session start: 2025-01-09 19:32:08
Session start: 2025-01-09 19:32:08
Session start: 2025-01-09 19:32:23
Session start: 2025-01-09 19:32:23
Session start: 2025-01-09 19:32:23
Session start: 2025-01-09 19:32:24
Session start: 2025-01-09 19:32:24
Session start: 2025-01-09 19:42:58
XP and level updated: 35 XP, Level 2
Session end: 2025-01-09 20:03:12
Session start: 2025-01-10 12:54:39
XP and level updated: 45 XP, Level 2
Session end: 2025-01-10 12:56:40
Session start: 2025-01-10 12:56:44
XP and level updated: 55 XP, Level 2
Session end: 2025-01-10 12:57:08
Session start: 2025-01-10 12:58:33
XP and level updated: 65 XP, Level 2
Session end: 2025-01-10 12:58:47
Session start: 2025-01-10 12:58:58
XP and level updated: 75 XP, Level 2
Session end: 2025-01-10 12:59:20
Session start: 2025-01-10 12:59:22
XP and level updated: 85 XP, Level 2
Session end: 2025-01-10 12:59:44
Session start: 2025-01-10 13:05:23
XP and level updated: 95 XP, Level 2
Session end: 2025-01-10 13:05:37
Session start: 2025-01-10 13:06:37
XP and level updated: 105 XP, Level 2
Session end: 2025-01-10 13:06:52
Session start: 2025-01-10 13:16:09
Session start: 2025-01-10 13:16:09
Session start: 2025-01-10 13:16:09
Session start: 2025-01-10 13:16:09
Session start: 2025-01-10 13:16:10
Session start: 2025-01-14 18:59:04
XP and level updated: 100 XP, Level 2
Session end: 2025-01-14 19:23:41
Session start: 2025-01-15 07:47:49
Session start: 2025-01-15 07:47:49
Session start: 2025-01-15 07:47:50
Session start: 2025-01-15 07:47:50
Session start: 2025-01-15 07:47:51
Session start: 2025-01-15 07:55:49
XP and level updated: 110 XP, Level 2
Session end: 2025-01-15 08:17:28
Session start: 2025-01-15 14:59:38
Session start: 2025-01-15 14:59:38
Session start: 2025-01-15 14:59:38
Session start: 2025-01-15 14:59:39
Session start: 2025-01-15 14:59:46
Session start: 2025-01-15 14:59:46
Session start: 2025-01-15 14:59:46
Session start: 2025-01-15 14:59:47
Session start: 2025-01-15 14:59:47
Session start: 2025-01-15 21:39:12
Session start: 2025-01-15 21:39:13
Session start: 2025-01-15 21:39:13
Session start: 2025-01-15 21:39:13
Session start: 2025-01-15 21:39:18
Session start: 2025-01-15 21:39:18
Session start: 2025-01-15 21:39:19
Session start: 2025-01-15 21:39:19
Session start: 2025-01-15 21:39:20
Session start: 2025-01-17 15:21:48
Session start: 2025-01-17 15:21:48
Session start: 2025-01-17 15:21:48
Session start: 2025-01-17 15:21:49
Session start: 2025-01-17 15:21:52
Session start: 2025-01-17 15:21:53
Session start: 2025-01-17 15:21:53
Session start: 2025-01-17 15:21:53
Session start: 2025-01-17 15:21:53
Session start: 2025-01-17 19:15:35
Session start: 2025-01-17 19:15:36
Session start: 2025-01-17 19:15:36
Session start: 2025-01-17 19:15:36
Session start: 2025-01-17 19:15:42
Session start: 2025-01-17 19:15:42
Session start: 2025-01-17 19:15:42
Session start: 2025-01-17 19:15:42
Session start: 2025-01-17 19:15:42
Session start: 2025-01-17 19:20:19
Session end: 2025-01-17 19:20:25
Session start: 2025-01-17 19:20:35
Session start: 2025-01-17 19:24:39
Session start: 2025-01-17 19:24:49
XP and level updated: 120 XP, Level 2
Session end: 2025-01-17 19:46:00
Session start: 2025-01-17 19:46:11
XP and level updated: 130 XP, Level 2
Session end: 2025-01-17 19:46:24
Session start: 2025-01-17 19:46:34
Session end: 2025-01-17 19:46:38
Session start: 2025-01-17 19:46:48
XP and level updated: 125 XP, Level 2
Session start: 2025-01-18 20:21:53
Session start: 2025-01-18 20:22:03
XP and level updated: 135 XP, Level 2
Session start: 2025-01-18 20:26:51
Session start: 2025-01-18 20:27:18
Session start: 2025-01-18 20:28:48
Session start: 2025-01-18 20:31:24
Session start: 2025-01-18 20:33:13
XP and level updated: 145 XP, Level 2
XP and level updated: 155 XP, Level 2
Session end: 2025-01-18 20:33:50
Session start: 2025-01-18 20:35:58
XP and level updated: 165 XP, Level 2
Session end: 2025-01-18 20:36:18
Session start: 2025-01-18 20:36:20
XP and level updated: 175 XP, Level 2
Session end: 2025-01-18 20:36:35
Session start: 2025-01-18 20:38:28
XP and level updated: 170 XP, Level 2
Session end: 2025-01-18 20:38:47
Session start: 2025-01-18 20:41:32
XP and level updated: 180 XP, Level 2
Session end: 2025-01-18 20:42:35
Session start: 2025-01-18 20:42:41
Session end: 2025-01-18 20:42:49
Session start: 2025-01-18 20:44:43
XP and level updated: 175 XP, Level 2
Session end: 2025-01-18 20:45:05
Session start: 2025-01-18 20:45:09
XP and level updated: 185 XP, Level 2
Session end: 2025-01-18 20:45:22
Session start: 2025-01-18 20:45:58
XP and level updated: 180 XP, Level 2
Session end: 2025-01-18 20:46:12
Session start: 2025-01-18 20:46:15
XP and level updated: 190 XP, Level 2
Session end: 2025-01-18 20:46:28
Session end: 2025-01-18 20:47:18
Session start: 2025-01-18 20:47:29
XP and level updated: 0 XP, Level 3
Session end: 2025-01-18 20:47:46
Session start: 2025-01-18 20:47:52
Session start: 2025-01-18 20:47:56
Session end: 2025-01-18 20:48:06
Session end: 2025-01-18 20:48:09
Session start: 2025-01-18 20:48:19
Session end: 2025-01-18 20:48:25
Session start: 2025-01-18 20:48:35
Session end: 2025-01-18 20:48:51
Session start: 2025-01-18 20:49:01
XP and level updated: 10 XP, Level 3
Session end: 2025-01-18 20:49:13
Session start: 2025-01-18 20:49:23
XP and level updated: 20 XP, Level 3
Session end: 2025-01-18 20:49:30
Session start: 2025-01-18 20:49:37
Session start: 2025-01-18 20:49:40
XP and level updated: 15 XP, Level 3
Session end: 2025-01-18 20:49:53
XP and level updated: 25 XP, Level 3
Session end: 2025-01-18 20:50:03
Session start: 2025-01-18 20:50:03
Session end: 2025-01-18 20:50:07
Session start: 2025-01-18 20:50:17
XP and level updated: 35 XP, Level 3
Session end: 2025-01-18 20:51:43
Session start: 2025-01-18 20:51:53
XP and level updated: 45 XP, Level 3
Session end: 2025-01-18 20:52:04
Session start: 2025-01-18 20:52:14
XP and level updated: 55 XP, Level 3
Session end: 2025-01-18 20:52:23
Session start: 2025-01-18 20:52:33
Session end: 2025-01-18 20:52:37
Session start: 2025-01-18 20:52:47
Session start: 2025-01-18 20:53:03
XP and level updated: 50 XP, Level 3
Session end: 2025-01-18 20:53:17
XP and level updated: 60 XP, Level 3
Session end: 2025-01-18 20:53:39
Session start: 2025-01-18 20:53:49
Session end: 2025-01-18 20:53:55
Session start: 2025-01-18 20:54:05
Session end: 2025-01-18 20:54:43
Session start: 2025-01-18 20:54:53
Session end: 2025-01-18 20:55:04
Session start: 2025-01-18 20:55:15
Session end: 2025-01-18 20:55:27
Session start: 2025-01-18 20:55:37
Session end: 2025-01-18 20:56:02
Session start: 2025-01-18 20:56:13
Session duration: 0 minutes and 3 seconds
Session end: 2025-01-18 20:56:16
Session start: 2025-01-18 20:56:17
Session start: 2025-01-18 20:56:26
Session duration: 0 minutes and 3 seconds
Session end: 2025-01-18 20:56:30
XP and level updated: 70 XP, Level 3
Session start: 2025-01-18 20:56:40
Session duration: 0 minutes and 3 seconds
Session end: 2025-01-18 20:56:44
Session duration: 0 minutes and 26 seconds
Session end: 2025-01-18 20:56:44
Session start: 2025-01-18 20:56:54
XP and level updated: 80 XP, Level 3
Session duration: 0 minutes and 10 seconds
Session end: 2025-01-18 20:57:04
Session start: 2025-01-18 20:57:14
Session start: 2025-01-18 21:00:19
XP and level updated: 90 XP, Level 3
Session duration: 0 minutes and 13 seconds
Session end: 2025-01-18 21:00:33
Session start: 2025-01-18 21:01:07
Session duration: 0 minutes and 4 seconds
Session end: 2025-01-18 21:01:12
Session start: 2025-01-18 21:01:22
XP and level updated: 100 XP, Level 3
Session duration: 0 minutes and 10 seconds
Session end: 2025-01-18 21:01:33
Session start: 2025-01-18 21:01:43
XP and level updated: 95 XP, Level 3
Session duration: 0 minutes and 11 seconds
Session end: 2025-01-18 21:01:54
Session start: 2025-01-18 21:02:04
Session start: 2025-01-18 21:04:01
XP and level updated: 105 XP, Level 3
Session duration: 0 minutes and 12 seconds
Session end: 2025-01-18 21:04:14
Session start: 2025-01-18 21:15:18
XP and level updated: 115 XP, Level 3
Session duration: 0 minutes and 14 seconds
Session end: 2025-01-18 21:15:33
Session start: 2025-01-19 15:13:11
Session start: 2025-01-19 15:18:00
XP and level updated: 125 XP, Level 3
Session duration: 20 minutes and 0 seconds
Session end: 2025-01-19 15:38:03
Session start: 2025-01-23 19:41:17
Session start: 2025-01-24 07:02:57
Session start: 2025-01-24 07:41:21
XP and level updated: 135 XP, Level 3
Session duration: 20 minutes and 0 seconds
Session end: 2025-01-24 08:01:25
Session start: 2025-01-24 12:52:39
Session start: 2025-01-29 08:20:29
Session start: 2025-02-02 01:14:42
Session start: 2025-02-07 07:22:13
Session start: 2025-02-07 08:16:24
XP and level updated: 145 XP, Level 3
Session duration: 20 minutes and 0 seconds
Session end: 2025-02-07 08:36:28
Session start: 2025-02-07 11:26:38
XP and level updated: 155 XP, Level 3
Session duration: 5 minutes and 58 seconds
Session end: 2025-02-07 11:32:38
Session start: 2025-02-07 15:13:45
XP and level updated: 165 XP, Level 3
Session duration: 0 minutes and 34 seconds
Session end: 2025-02-07 15:14:20
Session start: 2025-02-08 19:31:35
Session start: 2025-02-08 19:39:23
XP and level updated: 175 XP, Level 3
Session duration: 20 minutes and 0 seconds
Session end: 2025-02-08 19:59:27
Session start: 2025-02-09 19:55:14
XP and level updated: 185 XP, Level 3
Session duration: 60 minutes and 0 seconds
Session end: 2025-02-09 20:55:23
Session start: 2025-02-10 21:34:09
XP and level updated: 180 XP, Level 3
Session duration: 3 minutes and 49 seconds
Session end: 2025-02-10 21:37:58
Session start: 2025-02-10 21:38:33
XP and level updated: 190 XP, Level 3
Session duration: 0 minutes and 17 seconds
Session end: 2025-02-10 21:38:51
Session start: 2025-02-11 19:31:01
XP and level updated: 185 XP, Level 3
XP and level updated: 180 XP, Level 3
Session duration: 60 minutes and 0 seconds
Session end: 2025-02-11 20:31:11
Session start: 2025-02-15 12:17:03
XP and level updated: 190 XP, Level 3
Session duration: 60 minutes and 0 seconds
Session end: 2025-02-15 13:17:13
Session start: 2025-02-15 15:27:46
Session start: 2025-02-15 15:37:36
XP and level updated: 185 XP, Level 3
Session start: 2025-02-15 20:58:49
Session start: 2025-02-15 21:08:40
XP and level updated: 180 XP, Level 3
Session duration: 0 minutes and 46 seconds
Session end: 2025-02-15 21:09:27
Session start: 2025-02-16 19:07:37
XP and level updated: 190 XP, Level 3
Session duration: 60 minutes and 0 seconds
Session end: 2025-02-16 20:07:45
Session start: 2025-03-10 18:25:38
XP and level updated: 185 XP, Level 3
Session duration: 0 minutes and 40 seconds
Session end: 2025-03-10 18:26:18
Session start: 2025-03-20 20:16:17
Session duration: 0 minutes and 4 seconds
Session end: 2025-03-20 20:16:22
Session start: 2025-03-21 09:32:07
XP and level updated: 195 XP, Level 3
Session duration: 0 minutes and 6 seconds
Session end: 2025-03-21 09:32:14
Session start: 2025-03-21 20:32:49
XP and level updated: 190 XP, Level 3
XP and level updated: 185 XP, Level 3
Session duration: 0 minutes and 12 seconds
Session end: 2025-03-21 20:33:02
Session start: 2025-03-21 21:52:31
Session duration: 0 minutes and 3 seconds
Session end: 2025-03-21 21:52:35
Session start: 2025-03-22 01:18:00
XP and level updated: 180 XP, Level 3
Session duration: 0 minutes and 11 seconds
Session end: 2025-03-22 01:18:11
Session start: 2025-03-22 09:07:18
XP and level updated: 190 XP, Level 3
Session duration: 1 minutes and 31 seconds
Session end: 2025-03-22 09:08:50
Session start: 2025-03-23 09:24:39
Session duration: 60 minutes and 0 seconds
Session end: 2025-03-23 10:24:47
Session start: 2025-03-26 21:22:54
XP and level updated: 185 XP, Level 3
Session duration: 0 minutes and 44 seconds
Session end: 2025-03-26 21:23:39
Session start: 2025-04-09 21:13:39
XP and level updated: 195 XP, Level 3
Session duration: 0 minutes and 12 seconds
Session end: 2025-04-09 21:13:51
Session start: 2025-04-26 21:55:04
XP and level updated: 190 XP, Level 3
Session duration: 1 minutes and 58 seconds
Session end: 2025-04-26 21:57:03
Session start: 2025-04-30 23:49:24
XP and level updated: 200 XP, Level 3
Session duration: 0 minutes and 13 seconds
Session end: 2025-04-30 23:49:37
Session start: 2025-05-01 18:27:02
Session duration: 60 minutes and 0 seconds
Session end: 2025-05-01 19:27:10
Session start: 2025-05-01 20:30:21
XP and level updated: 195 XP, Level 3
Session duration: 0 minutes and 11 seconds
Session end: 2025-05-01 20:30:32
Session start: 2025-06-17 22:44:00
XP and level updated: 205 XP, Level 3
Session duration: 0 minutes and 20 seconds
Session end: 2025-06-17 22:44:22
Session start: 2025-06-17 23:05:53
XP and level updated: 215 XP, Level 3
Session start: 2025-06-17 23:06:31
XP and level updated: 225 XP, Level 3
Session start: 2025-06-17 23:23:35
Session duration: 22 minutes and 43 seconds
Session end: 2025-06-17 23:29:19
Session start: 2025-06-17 23:34:04
Session start: 2025-06-17 23:37:04
Session end: 2025-06-17 23:37:04
XP and level updated: 235 XP, Level 3
XP and level updated: 245 XP, Level 3
Session start: 2025-06-17 23:42:39
Session end: 2025-06-17 23:42:39
XP and level updated: 240 XP, Level 3
Session Ellipsis: 2025-06-17 23:42:54
Session Ellipsis: 2025-06-17 23:42:54
Session Ellipsis: 2025-06-17 23:42:55
Session start: 2025-06-17 23:43:21
Session end: 2025-06-17 23:43:21
XP and level updated: 250 XP, Level 3
Session Ellipsis: 2025-06-17 23:44:59
Session Ellipsis: 2025-06-17 23:44:59
Session Ellipsis: 2025-06-17 23:44:59
Session end: 2025-08-15 10:19:52

View File

@@ -1 +1 @@
28955
0

View File

@@ -1 +1 @@
250
0

540
main.py
View File

@@ -1,27 +1,236 @@
# Migrate questions from file to SQLite if not already present
def migrate_questions_to_sqlite():
import sqlite3
if not os.path.exists(QUESTIONS_FILE):
return
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS questions (
id INTEGER PRIMARY KEY,
category TEXT,
question TEXT,
answer1 TEXT,
answer2 TEXT,
answer3 TEXT,
correct_index INTEGER
)''')
# Only import if table is empty
c.execute('SELECT COUNT(*) FROM questions')
if c.fetchone()[0] == 0:
with open(QUESTIONS_FILE) as f:
for line in f:
parts = [p.strip() for p in line.strip().split('|')]
if len(parts) == 7:
# category|question|a1|a2|a3|correct|explanation
category, question = parts[0], parts[1]
answers = parts[2:5]
correct_index = int(parts[5])
explanation = parts[6]
elif len(parts) == 6:
# question|a1|a2|a3|correct|explanation (no category)
category = "General"
question = parts[0]
answers = parts[1:4]
correct_index = int(parts[4])
explanation = parts[5]
else:
continue
while len(answers) < 3:
answers.append("")
# Add explanation as a new column if not exists
try:
c.execute('ALTER TABLE questions ADD COLUMN explanation TEXT')
except Exception:
pass
c.execute('INSERT INTO questions (category, question, answer1, answer2, answer3, correct_index, explanation) VALUES (?, ?, ?, ?, ?, ?, ?)',
(category, question, answers[0], answers[1], answers[2], correct_index, explanation))
conn.commit()
conn.close()
# Data migration from text files to SQLite
def migrate_txt_to_sqlite():
migrated = False
# Migrate XP, level, total_time
xp = 0
level = 1
total_time = 0
if os.path.exists(XP_FILE):
try:
with open(XP_FILE) as f:
xp = int(f.read().strip() or 0)
except Exception:
pass
migrated = True
if os.path.exists(LEVEL_FILE):
try:
with open(LEVEL_FILE) as f:
level = int(f.read().strip() or 1)
except Exception:
pass
migrated = True
if os.path.exists(TOTAL_TIME_FILE):
try:
with open(TOTAL_TIME_FILE) as f:
total_time = int(f.read().strip() or 0)
except Exception:
pass
migrated = True
# Save to SQLite if any file existed
if migrated:
stats = UserStatsSqlite(DB_PATH, SUBSCRIPTION_END_DATE)
stats.save_stats(xp, level, total_time)
# Migrate session log
if os.path.exists(SESSION_LOG):
stats = UserStatsSqlite(DB_PATH, SUBSCRIPTION_END_DATE)
try:
with open(SESSION_LOG) as f:
for line in f:
line = line.strip()
if line:
# Try to parse: Session start/end: YYYY-MM-DD HH:MM:SS
import re
m = re.match(r'Session (\w+): (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', line)
if m:
status, timestamp = m.groups()
stats.log_session(status, duration=None)
except Exception:
pass
migrated = True
# Remove text files
if migrated:
for f in [XP_FILE, LEVEL_FILE, TOTAL_TIME_FILE, SESSION_LOG]:
try:
os.remove(f)
except Exception:
pass
import os
import time
import psutil
import tkinter as tk
from tkinter import messagebox
import random
import subprocess
from datetime import datetime, timedelta
import webbrowser
import random
import sqlite3
TIMER_MINUTES = 30 # Default quiz timer in minutes
# Constants
def show_score_page(score_by_category, total_score):
# Minimal implementation: show a message box with the score summary
import tkinter as tk
from tkinter import messagebox
summary = f"Total Score: {total_score}\n"
for cat, (correct, total) in score_by_category.items():
percent = (correct / total * 100) if total else 0
summary += f"{cat}: {correct}/{total} ({percent:.0f}%)\n"
messagebox.showinfo("Quiz Complete", summary)
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
ASSETS_DIR = os.path.join(SCRIPT_DIR, 'assets')
XP_FILE = os.path.join(ASSETS_DIR, 'xp.txt')
LEVEL_FILE = os.path.join(ASSETS_DIR, 'level.txt')
SESSION_LOG = os.path.join(ASSETS_DIR, 'session.log')
TOTAL_TIME_FILE = os.path.join(ASSETS_DIR, 'total_time.txt')
QUESTIONS_FILE = os.path.join(ASSETS_DIR, 'questions.txt')
QUESTIONS_SHUFFLED_FILE = os.path.join(ASSETS_DIR, 'questions_shuffled.txt')
TOTAL_TIME_FILE = os.path.join(ASSETS_DIR, 'total_time.txt')
URL = "https://rol.redhat.com"
TIMER_MINUTES = 60
DB_PATH = os.path.join(SCRIPT_DIR, 'rhel_learning.db')
SUBSCRIPTION_END_DATE = datetime(2026, 5, 26)
# Set the ROL subscription expiration date
SUBSCRIPTION_END_DATE = datetime.now() + timedelta(days=343)
def format_time(seconds):
hours, remainder = divmod(seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return f"{hours}h {minutes}m {seconds}s"
# SQLite-based stats and session tracking
class UserStatsSqlite:
def __init__(self, db_path, subscription_end_date):
self.db_path = db_path
self.subscription_end_date = subscription_end_date
self._ensure_tables()
self._ensure_user()
def _connect(self):
return sqlite3.connect(self.db_path)
def _ensure_tables(self):
with self._connect() as conn:
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS user_stats (
id INTEGER PRIMARY KEY,
xp INTEGER NOT NULL,
level INTEGER NOT NULL,
total_time INTEGER NOT NULL
)''')
c.execute('''CREATE TABLE IF NOT EXISTS session_log (
id INTEGER PRIMARY KEY,
status TEXT,
timestamp TEXT,
duration INTEGER
)''')
conn.commit()
def _ensure_user(self):
with self._connect() as conn:
c = conn.cursor()
c.execute('SELECT COUNT(*) FROM user_stats')
if c.fetchone()[0] == 0:
c.execute('INSERT INTO user_stats (xp, level, total_time) VALUES (?, ?, ?)', (0, 1, 0))
conn.commit()
def get_stats(self):
with self._connect() as conn:
c = conn.cursor()
c.execute('SELECT xp, level, total_time FROM user_stats LIMIT 1')
return c.fetchone()
def save_stats(self, xp, level, total_time):
with self._connect() as conn:
c = conn.cursor()
c.execute('UPDATE user_stats SET xp=?, level=?, total_time=? WHERE id=1', (xp, level, total_time))
conn.commit()
def add_xp(self, amount):
xp, level, total_time = self.get_stats()
xp += amount
leveled_up = False
if xp >= level * 100:
xp -= level * 100
level += 1
leveled_up = True
self.save_stats(xp, level, total_time)
return xp, level, leveled_up
def add_time(self, seconds):
xp, level, total_time = self.get_stats()
total_time += seconds
self.save_stats(xp, level, total_time)
def days_left(self):
from datetime import datetime
today = datetime.now()
return (self.subscription_end_date - today).days
def log_session(self, status, duration=None):
import time
with self._connect() as conn:
c = conn.cursor()
c.execute('INSERT INTO session_log (status, timestamp, duration) VALUES (?, ?, ?)',
(status, time.strftime('%Y-%m-%d %H:%M:%S'), duration))
conn.commit()
def get_total_time(self):
_, _, total_time = self.get_stats()
return total_time
def get_xp_level(self):
xp, level, _ = self.get_stats()
return xp, level
import os
import time
import psutil
import tkinter as tk
from tkinter import messagebox
# Utility Functions
def initialize_assets():
@@ -46,32 +255,15 @@ def log_session(status):
def display_summary():
"""Display a summary of total time, XP, and level."""
try:
with open(TOTAL_TIME_FILE, 'r') as f:
total_time = int(f.read().strip() or 0)
except FileNotFoundError:
total_time = 0
try:
with open(XP_FILE, 'r') as f:
xp = int(f.read().strip() or 0)
except FileNotFoundError:
xp = 0
try:
with open(LEVEL_FILE, 'r') as f:
level = int(f.read().strip() or 1)
except FileNotFoundError:
level = 1
hours, remainder = divmod(total_time, 3600)
minutes, seconds = divmod(remainder, 60)
stats = UserStatsSqlite(DB_PATH, SUBSCRIPTION_END_DATE)
print(f"Welcome to the RHEL Learning Script!")
print(f"Total session time: {hours} hours, {minutes} minutes, and {seconds} seconds.")
total_time = stats.get_total_time()
xp, level = stats.get_xp_level()
print(f"Total session time: {format_time(total_time)}")
print(f"Current Level: {level}")
print(f"Current XP: {xp}")
print(f"Days left: {stats.days_left()}")
print()
return total_time, xp, level
def kill_steam():
@@ -83,29 +275,18 @@ def kill_steam():
def update_xp(correct):
"""Update XP and level based on whether the question was answered correctly."""
with open(XP_FILE, 'r') as f:
xp = int(f.read().strip() or 0)
with open(LEVEL_FILE, 'r') as f:
level = int(f.read().strip() or 1)
stats = UserStatsSqlite(DB_PATH, SUBSCRIPTION_END_DATE)
if correct:
xp += 10
xp, level, leveled_up = stats.add_xp(10)
else:
xp, level, _ = stats.get_xp_level()
xp = max(0, xp - 5)
if xp >= level * 100:
level += 1
xp -= (level - 1) * 100
stats.save_stats(xp, level, stats.get_total_time())
leveled_up = False
if correct and leveled_up:
messagebox.showinfo("LEVEL UP!", f"LEVEL UP! You are now Level {level}!")
with open(XP_FILE, 'w') as f:
f.write(str(xp))
with open(LEVEL_FILE, 'w') as f:
f.write(str(level))
with open(SESSION_LOG, 'a') as f:
f.write(f"XP and level updated: {xp} XP, Level {level}\n")
return xp, level
def show_timer(minutes, questions_file, session_log, total_time, xp, level, root):
@@ -116,21 +297,23 @@ def show_timer(minutes, questions_file, session_log, total_time, xp, level, root
elapsed_time += 1
hours, remainder = divmod(seconds_left, 3600)
minutes_left, seconds = divmod(remainder, 60)
timer_label.config(text=f"Time left: {hours:02}:{minutes_left:02}:{seconds:02}")
root.after(1000, update_timer)
# Only update timer_label if it still exists
if timer_label.winfo_exists():
timer_label.config(text=f"Time left: {hours:02}:{minutes_left:02}:{seconds:02}")
root.after(1000, update_timer)
else:
end_session()
def update_rol_timer():
today = datetime.now()
days_left = (SUBSCRIPTION_END_DATE - today).days
if days_left > 0:
rol_label.config(text=f"ROL Subscription: {days_left} days left")
root.after(60000, update_rol_timer) # Update every minute
else:
rol_label.config(text="ROL Subscription expired!")
messagebox.showwarning("Subscription Alert", "Your ROL Subscription has expired!")
if rol_label.winfo_exists():
if days_left > 0:
rol_label.config(text=f"ROL Subscription: {days_left} days left")
root.after(60000, update_rol_timer) # Update every minute
else:
rol_label.config(text="ROL Subscription expired!")
messagebox.showwarning("Subscription Alert", "Your ROL Subscription has expired!")
def end_session():
with open(session_log, 'a') as f:
@@ -139,52 +322,147 @@ def show_timer(minutes, questions_file, session_log, total_time, xp, level, root
f.write(str(total_time + elapsed_time))
root.destroy()
def on_closing():
end_session()
# Load & parse questions
with open(questions_file, 'r') as f:
lines = [l.strip() for l in f if l.strip()]
question_line = random.choice(lines)
parts = question_line.split('|')
question_text = parts[0]
answers = parts[1:-1]
correct_idx = int(parts[-1]) - 1 # zero-based
# Build a list of (text, is_correct) and shuffle
opts = []
for idx, ans in enumerate(answers):
opts.append({'text': ans, 'correct': (idx == correct_idx)})
random.shuffle(opts)
# Display question
question_label = tk.Label(root, text=f"Question: {question_text}", font=("Helvetica", 14))
question_label.pack(pady=10)
answer_var = tk.StringVar(value="")
radio_buttons = []
for opt in opts:
rb = tk.Radiobutton(root, text=opt['text'],
variable=answer_var, value=opt['text'],
font=("Helvetica", 12))
rb.pack(anchor='w')
radio_buttons.append(rb)
def check_answer():
sel = answer_var.get()
correct = any(o['text'] == sel and o['correct'] for o in opts)
if correct:
messagebox.showinfo("Correct!", "You chose the right answer.")
new_xp, new_level = update_xp(True)
# Function body starts here
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
# Try to fetch explanation if present
try:
c.execute('SELECT id, category, question, answer1, answer2, answer3, correct_index, explanation FROM questions')
rows = c.fetchall()
has_explanation = True
except Exception:
c.execute('SELECT id, category, question, answer1, answer2, answer3, correct_index FROM questions')
rows = c.fetchall()
has_explanation = False
conn.close()
if not rows:
messagebox.showerror("Error", "No questions found in database. Please populate your questions file.")
root.destroy()
return
# Convert to lines format for compatibility
lines = []
for row in rows:
# Compose as: Category|Question|A|B|C|CorrectIndex|Explanation (if present)
if has_explanation:
lines.append(f"{row[1]}|{row[2]}|{row[3]}|{row[4]}|{row[5]}|{row[6]}|{row[7]}")
else:
messagebox.showinfo("Incorrect", "Sorry, thats not correct.")
new_xp, new_level = update_xp(False)
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}")
lines.append(f"{row[1]}|{row[2]}|{row[3]}|{row[4]}|{row[5]}|{row[6]}")
submit_btn = tk.Button(root, text="Submit", command=check_answer)
submit_btn.pack(pady=5)
# Track per-category score
score_by_category = {}
total_correct = 0
total_questions = 0
max_score = 300
# Show all questions in random order
random.shuffle(lines)
questions_to_ask = lines
question_idx = 0
# Create a frame for question/answer widgets ONCE
question_frame = tk.Frame(root)
question_frame.pack(pady=10)
def ask_next_question():
nonlocal question_idx, total_questions
nonlocal total_correct, total_questions
if question_idx >= len(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]
parts = qline.split('|')
if len(parts) < 5:
question_idx += 1
ask_next_question()
return
# Assume format: Category|Question|A|B|C|CorrectIndex|Explanation (optional)
category = parts[0] if parts[0] else "General"
question_text = parts[1]
answers = parts[2:-1] if len(parts) < 7 else parts[2:-2]
correct_idx = int(parts[-2]) - 1 if len(parts) >= 7 else int(parts[-1]) - 1
explanation = parts[-1] if len(parts) >= 7 else None
# Build a list of (text, is_correct) and shuffle
opts = []
for idx, ans in enumerate(answers):
if ans.strip():
opts.append({'text': ans, 'correct': (idx == correct_idx)})
random.shuffle(opts)
# Clear previous question/answer widgets only
for widget in question_frame.winfo_children():
widget.destroy()
# Show only the question text (not category)
question_label = tk.Label(question_frame, text=question_text, font=("Helvetica", 14))
question_label.pack(pady=10)
answer_var = tk.StringVar(value="")
radio_buttons = []
for opt in opts:
rb = tk.Radiobutton(question_frame, text=opt['text'],
variable=answer_var, value=opt['text'],
font=("Helvetica", 12))
rb.pack(anchor='w')
radio_buttons.append(rb)
def check_answer():
nonlocal question_idx, total_questions, total_correct
sel = answer_var.get()
correct = any(o['text'] == sel and o['correct'] for o in opts)
# Find the correct answer
correct_answer = None
for o in opts:
if o['correct']:
correct_answer = o['text']
break
# Use explanation if present, else fallback
if explanation:
detail = explanation
else:
detail = f"The correct answer is: {correct_answer}"
if correct:
messagebox.showinfo("Correct!", f"You chose the right answer.\n{detail}")
new_xp, new_level = update_xp(True)
score_by_category.setdefault(category, [0, 0])[0] += 1
total_correct += 1
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()])
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)
rol_label = tk.Label(root, text=f"ROL Subscription: {stats.days_left()} days left", font=("Helvetica", 14), fg="red")
rol_label.pack(pady=5)
xp_val, level_val = stats.get_xp_level()
xp_label = tk.Label(root, text=f"XP: {xp_val}", font=("Helvetica", 14))
xp_label.pack(pady=5)
level_label = tk.Label(root, text=f"Level: {level_val}", font=("Helvetica", 14))
level_label.pack(pady=5)
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
ask_next_question()
def periodic_kill_steam():
kill_steam()
@@ -193,37 +471,12 @@ def show_timer(minutes, questions_file, session_log, total_time, xp, level, root
seconds_left = minutes * 60
elapsed_time = 0
# GUI Components
timer_label = tk.Label(root, text=f"Time left: {minutes:02}:00:00", font=("Helvetica", 16))
timer_label.pack(pady=20)
rol_label = tk.Label(root, text="ROL Subscription: Calculating...", font=("Helvetica", 14), fg="red")
rol_label.pack(pady=5)
xp_label = tk.Label(root, text=f"XP: {xp}", font=("Helvetica", 14))
xp_label.pack(pady=5)
level_label = tk.Label(root, text=f"Level: {level}", font=("Helvetica", 14))
level_label.pack(pady=5)
total_time_label = tk.Label(root, text=f"Total session time: {total_time // 3600} hours, {(total_time % 3600) // 60} minutes, and {total_time % 60} seconds", font=("Helvetica", 14))
total_time_label.pack(pady=5)
root.after(1000, update_timer)
root.after(1000, update_rol_timer)
root.after(10000, periodic_kill_steam)
def open_firefox(url):
print(f"Attempting to open Firefox with URL: {url}")
if 'DISPLAY' in os.environ:
try:
subprocess.Popen(['firefox', '--new-window', url], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
print("Firefox opened successfully.")
except subprocess.CalledProcessError:
print("Firefox is already running. Reusing the existing instance.")
subprocess.Popen(['firefox', '--new-tab', url], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
else:
print("No graphical environment detected. Skipping browser opening.")
## Removed open_firefox, use open_learning_links instead
def shuffle_questions(input_file, output_file):
"""Shuffle questions and answers, updating the correct answer index."""
@@ -233,12 +486,25 @@ def shuffle_questions(input_file, output_file):
with open(output_file, 'w') as f:
for line in lines:
parts = line.split('|')
q, answers, correct = parts[0], parts[1:-1], int(parts[-1])
# Handle both with and without category
if len(parts) == 6:
# category|question|a1|a2|a3|correct
category, question = parts[0], parts[1]
answers = parts[2:5]
correct = int(parts[5])
elif len(parts) == 5:
# question|a1|a2|a3|correct (no category)
category = "General"
question = parts[0]
answers = parts[1:4]
correct = int(parts[4])
else:
continue # skip malformed
zipped = list(zip(answers, range(1, len(answers)+1)))
random.shuffle(zipped)
new_answers, old_indices = zip(*zipped)
new_correct = old_indices.index(correct) + 1
f.write(f"{q}|{'|'.join(new_answers)}|{new_correct}\n")
f.write(f"{category}|{question}|{'|'.join(new_answers)}|{new_correct}\n")
def open_learning_links():
urls = [
@@ -249,18 +515,28 @@ def open_learning_links():
webbrowser.open_new_tab(url)
def main():
migrate_txt_to_sqlite()
migrate_questions_to_sqlite()
initialize_assets()
log_session("start")
total_time, xp, level = display_summary()
kill_steam()
open_firefox(URL)
# open_firefox removed; open_learning_links is used instead
open_learning_links()
root = tk.Tk()
root.title("RHEL Learning Timer")
root.protocol("WM_DELETE_WINDOW", lambda: log_session(...))
show_timer(TIMER_MINUTES, QUESTIONS_SHUFFLED_FILE, SESSION_LOG, total_time, xp, level, root)
log_session("end")
root.mainloop()
def on_closing():
log_session("end")
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
try:
show_timer(TIMER_MINUTES, QUESTIONS_SHUFFLED_FILE, SESSION_LOG, total_time, xp, level, root)
root.mainloop()
except Exception as e:
messagebox.showerror("Error", f"An error occurred: {e}")
log_session("end")
if __name__ == "__main__":
shuffle_questions(QUESTIONS_FILE, QUESTIONS_SHUFFLED_FILE)

63
migrate_to_sqlite.py Normal file
View File

@@ -0,0 +1,63 @@
import os
from datetime import datetime
from main import (
XP_FILE, LEVEL_FILE, TOTAL_TIME_FILE, SESSION_LOG, DB_PATH, SUBSCRIPTION_END_DATE, UserStatsSqlite
)
def migrate_txt_to_sqlite():
migrated = False
xp = 0
level = 1
total_time = 0
if os.path.exists(XP_FILE):
try:
with open(XP_FILE) as f:
xp = int(f.read().strip() or 0)
except Exception:
pass
migrated = True
if os.path.exists(LEVEL_FILE):
try:
with open(LEVEL_FILE) as f:
level = int(f.read().strip() or 1)
except Exception:
pass
migrated = True
if os.path.exists(TOTAL_TIME_FILE):
try:
with open(TOTAL_TIME_FILE) as f:
total_time = int(f.read().strip() or 0)
except Exception:
pass
migrated = True
if migrated:
stats = UserStatsSqlite(DB_PATH, SUBSCRIPTION_END_DATE)
stats.save_stats(xp, level, total_time)
if os.path.exists(SESSION_LOG):
stats = UserStatsSqlite(DB_PATH, SUBSCRIPTION_END_DATE)
try:
with open(SESSION_LOG) as f:
for line in f:
line = line.strip()
if line:
import re
m = re.match(r'Session (\w+): (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', line)
if m:
status, timestamp = m.groups()
stats.log_session(status, duration=None)
except Exception:
pass
migrated = True
if migrated:
for f in [XP_FILE, LEVEL_FILE, TOTAL_TIME_FILE, SESSION_LOG]:
try:
os.remove(f)
except Exception:
pass
if migrated:
print("Migration complete. Data moved to SQLite and text files removed.")
else:
print("No migration needed. No text files found.")
if __name__ == "__main__":
migrate_txt_to_sqlite()

BIN
rhel_learning.db Normal file

Binary file not shown.

View File

@@ -1,139 +0,0 @@
import os
import time
import psutil
import tkinter as tk
import random
import subprocess
# Utility functions
def initialize_assets(assets_dir, xp_file, level_file, session_log, total_time_file, questions_file):
if not os.path.exists(assets_dir):
os.makedirs(assets_dir)
if not os.path.exists(xp_file):
with open(xp_file, 'w') as f:
f.write('0')
if not os.path.exists(level_file):
with open(level_file, 'w') as f:
f.write('1')
if not os.path.exists(session_log):
open(session_log, 'w').close()
if not os.path.exists(total_time_file):
with open(total_time_file, 'w') as f:
f.write('0')
if not os.path.exists(questions_file):
raise FileNotFoundError(f"Questions file not found: {questions_file}")
def log_session(session_log, status):
with open(session_log, 'a') as f:
f.write(f"Session {status}: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
def display_summary(total_time_file, xp_file, level_file):
with open(total_time_file, 'r') as f:
total_time = int(f.read().strip())
with open(xp_file, 'r') as f:
xp = int(f.read().strip())
with open(level_file, 'r') as f:
level = int(f.read().strip())
hours, minutes = divmod(total_time, 60)
print(f"Welcome to the RHEL Learning Script!")
print(f"Total session time: {hours} hours and {minutes} minutes.")
print(f"Current Level: {level}")
print(f"Current XP: {xp}")
print()
def kill_steam():
for proc in psutil.process_iter():
if proc.name() == "steam.exe":
proc.kill()
def show_timer(minutes, questions_file, session_log):
print(f"The session will last {minutes} minutes.")
# Open the URL in the default web browser
subprocess.Popen(['xdg-open', URL])
# Create a simple timer using tkinter
root = tk.Tk()
root.title("Timer")
label = tk.Label(root, text="", font=("Helvetica", 48))
label.pack()
def update_timer():
nonlocal minutes
if minutes > 0:
minutes -= 1
label.config(text=f"{minutes} minutes remaining")
root.after(60000, update_timer)
else:
label.config(text="Time's up!")
root.after(1000, root.destroy)
update_timer()
root.mainloop()
# Simulate answering questions
with open(questions_file, 'r') as f:
questions = f.readlines()
random.shuffle(questions)
for question in questions[:5]: # Assume we ask 5 questions
print(question.strip())
time.sleep(2) # Simulate time taken to answer
with open(session_log, 'a') as f:
f.write(f"Session duration: {minutes} minutes\n")
def add_xp(xp_file, level_file, session_log):
with open(xp_file, 'r') as f:
xp = int(f.read().strip())
with open(level_file, 'r') as f:
level = int(f.read().strip())
new_xp = xp + 10
if new_xp >= level * 100:
level += 1
new_xp -= (level - 1) * 100
print(f"LEVEL UP! You are now Level {level}!")
with open(xp_file, 'w') as f:
f.write(str(new_xp))
with open(level_file, 'w') as f:
f.write(str(level))
with open(session_log, 'a') as f:
f.write(f"XP and level updated: {new_xp} XP, Level {level}\n")
# Constants
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
ASSETS_DIR = os.path.join(SCRIPT_DIR, 'assets')
XP_FILE = os.path.join(ASSETS_DIR, 'xp.txt')
LEVEL_FILE = os.path.join(ASSETS_DIR, 'level.txt')
SESSION_LOG = os.path.join(ASSETS_DIR, 'session.log')
QUESTIONS_FILE = os.path.join(ASSETS_DIR, 'questions.txt')
TOTAL_TIME_FILE = os.path.join(ASSETS_DIR, 'total_time.txt')
URL = "https://rol.redhat.com"
TIMER_MINUTES = 20
def main():
# Initialize assets
initialize_assets(ASSETS_DIR, XP_FILE, LEVEL_FILE, SESSION_LOG, TOTAL_TIME_FILE, QUESTIONS_FILE)
# Log session start
log_session(SESSION_LOG, "start")
# Display session summary
display_summary(TOTAL_TIME_FILE, XP_FILE, LEVEL_FILE)
# Start killing Steam processes in the background
kill_steam()
# Start session
print("Starting RHEL Learning Session...")
show_timer(TIMER_MINUTES, QUESTIONS_FILE, SESSION_LOG)
# Add XP
add_xp(XP_FILE, LEVEL_FILE, SESSION_LOG)
# Log session end
log_session(SESSION_LOG, "end")
if __name__ == "__main__":
main()