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:
BIN
__pycache__/main.cpython-312.pyc
Normal file
BIN
__pycache__/main.cpython-312.pyc
Normal file
Binary file not shown.
@@ -1 +1 @@
|
||||
3
|
||||
1
|
||||
@@ -18,3 +18,22 @@ How do you set a service to start at boot?|systemctl enable|systemctl start|syst
|
||||
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
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
28955
|
||||
0
|
||||
@@ -1 +1 @@
|
||||
250
|
||||
0
|
||||
468
main.py
468
main.py
@@ -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,6 +297,8 @@ 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)
|
||||
# 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:
|
||||
@@ -124,7 +307,7 @@ def show_timer(minutes, questions_file, session_log, total_time, xp, level, root
|
||||
def update_rol_timer():
|
||||
today = datetime.now()
|
||||
days_left = (SUBSCRIPTION_END_DATE - today).days
|
||||
|
||||
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
|
||||
@@ -139,53 +322,148 @@ 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()
|
||||
# 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:
|
||||
lines.append(f"{row[1]}|{row[2]}|{row[3]}|{row[4]}|{row[5]}|{row[6]}")
|
||||
|
||||
# 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
|
||||
# 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)
|
||||
|
||||
# Display question
|
||||
question_label = tk.Label(root, text=f"Question: {question_text}", font=("Helvetica", 14))
|
||||
# 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(root, text=opt['text'],
|
||||
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)
|
||||
if correct:
|
||||
messagebox.showinfo("Correct!", "You chose the right answer.")
|
||||
new_xp, new_level = update_xp(True)
|
||||
# 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:
|
||||
messagebox.showinfo("Incorrect", "Sorry, that’s not correct.")
|
||||
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(root, text="Submit", command=check_answer)
|
||||
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()
|
||||
root.after(10000, periodic_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)
|
||||
|
||||
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
63
migrate_to_sqlite.py
Normal 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
BIN
rhel_learning.db
Normal file
Binary file not shown.
139
src/main.py
139
src/main.py
@@ -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()
|
||||
Reference in New Issue
Block a user