香港理工大学实验室定时预约

香港理工大学实验室定时预约

文章目录

对爬虫、逆向感兴趣的同学可以查看文章,一对一小班教学(系统理论和实战教程)、提供接单兼职渠道:https://blog.csdn.net/weixin_35770067/article/details/142514698

简介

香港理工大学实验室预约项目,由于平时预约人数较多,因此定制定时预约软件,学校网页非常简单,一个简单的post请求直接搞定。

接单价格

有第三方抽佣,个人到手660,供大家参考。

软件界面

网站预约界面

代码

python 复制代码
# -*- coding: utf-8 -*-
'''
@Time    : 2025-06-23 16:24
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple bs4
'''
import time
import warnings
from bs4 import BeautifulSoup
import requests
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import threading
from datetime import datetime, timedelta
import ctypes
import sys
warnings.filterwarnings('ignore')
def hide_console():
    """隐藏控制台窗口"""
    if sys.platform.startswith('win'):
        handle = ctypes.windll.kernel32.GetConsoleWindow()
        ctypes.windll.user32.ShowWindow(handle, 0)

hide_console()

def create_order(instrument, cookies, auth_token, reservation_details):
    """
    Sends a POST request to create a reservation for the selected instrument.
    """
    # Dynamically set the URL and Referer based on the instrument choice
    if instrument == "fNIRS1":
        target_url = "https://urfms.polyu.edu.hk/facilities/ubsn/instruments/NIRSport_2_1/single_reservations"
        referer_url = "https://urfms.polyu.edu.hk/facilities/ubsn/instruments/NIRSport_2_1/single_reservations/new"
    else:  # Default to fNIRS2
        target_url = "https://urfms.polyu.edu.hk/facilities/ubsn/instruments/NIRSport_2_2/single_reservations"
        referer_url = "https://urfms.polyu.edu.hk/facilities/ubsn/instruments/NIRSport_2_2/single_reservations/new"

    headers = {
        'Host': 'urfms.polyu.edu.hk',
        'Cache-Control': 'max-age=0',
        'Origin': 'https://urfms.polyu.edu.hk',
        'Content-Type': 'application/x-www-form-urlencoded',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
        'Sec-Fetch-Site': 'same-origin',
        'Sec-Fetch-Mode': 'navigate',
        'Referer': referer_url,  # Use the dynamic Referer
        'Cookie': cookies
    }
    data = {
        'authenticity_token': auth_token,
        'order_account': '2204',
        'send_notification': '1',
        'commit': 'Create',
    }
    data.update(reservation_details)

    try:
        res = requests.post(target_url, headers=headers, data=data, verify=False, timeout=5)  # Use the dynamic URL
        res.raise_for_status()
        return res.text
    except requests.exceptions.RequestException:
        return ""


class ReservationApp:
    def __init__(self, root):
        self.root = root

        # --- Variables ---
        self.instrument_var = tk.StringVar(value="fNIRS2")  # Default selection
        self.start_date_var = tk.StringVar(value="13 Jul 2025")
        self.start_hour_var = tk.StringVar(value="9")
        self.start_min_var = tk.StringVar(value="00")
        self.start_meridian_var = tk.StringVar(value="AM")
        self.end_date_var = tk.StringVar(value="13 Jul 2025")
        self.end_hour_var = tk.StringVar(value="10")
        self.end_min_var = tk.StringVar(value="00")
        self.end_meridian_var = tk.StringVar(value="AM")
        self.duration_display_var = tk.StringVar(value="1:00")
        self.cookie_var = tk.StringVar()
        self.auth_token_var = tk.StringVar()
        self.schedule_time_var = tk.StringVar(value=f"{(datetime.now() + timedelta(minutes=1)).strftime('%H:%M:%S')}")
        self.status_var = tk.StringVar(value="Status: Idle")

        self._update_title()  # Set initial title
        self.root.geometry("850x420")  # Adjusted height for the new frame

        self.create_widgets()
        self.create_log_window()

    def _update_title(self):
        """Updates the main window title based on the instrument selection."""
        selected_instrument = self.instrument_var.get()
        self.root.title(f"Reservation Scheduler {selected_instrument}")

    def create_log_window(self):
        self.log_window = tk.Toplevel(self.root)
        self.log_window.title("Live HTML Log")
        self.log_window.geometry("800x600")
        self.log_text = scrolledtext.ScrolledText(self.log_window, wrap=tk.WORD, state=tk.DISABLED)
        self.log_text.pack(expand=True, fill='both')
        self.log_window.protocol("WM_DELETE_WINDOW", self.log_window.withdraw)
        self.log_window.withdraw()

    def update_status(self, message):
        self.status_var.set(message)

    def log_to_gui(self, message):
        self.log_text.config(state=tk.NORMAL)
        self.log_text.insert(tk.END, message)
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)

    def schedule_reservation(self):
        # Get all required values from GUI
        instrument = self.instrument_var.get()
        cookies = self.cookie_var.get()
        auth_token = self.auth_token_var.get()
        schedule_time_str = self.schedule_time_var.get()

        if not cookies or not auth_token:
            messagebox.showerror("Invalid Input", "Cookie and Authenticity Token fields cannot be empty.")
            return

        try:
            target_time = datetime.strptime(schedule_time_str, '%H:%M:%S').time()
            schedule_dt = datetime.combine(datetime.now().date(), target_time)
            if schedule_dt < datetime.now(): schedule_dt += timedelta(days=1)
            start_dt = schedule_dt - timedelta(seconds=5)
            delay = (start_dt - datetime.now()).total_seconds()
            if delay < 0:
                messagebox.showerror("Invalid Time", "Scheduled time is in the past.")
                return
        except ValueError:
            messagebox.showerror("Invalid Format", "Schedule time must be in HH:MM:SS format.")
            return

        try:
            duration_str = self.duration_display_var.get()
            h, m = map(int, duration_str.split(':')) if ':' in duration_str else (0, int(duration_str))
            total_minutes = h * 60 + m
        except ValueError:
            messagebox.showerror("Invalid Input", "Duration format is incorrect.")
            return

        reservation_details = {
            'reservation[reserve_start_date]': self.start_date_var.get(),
            'reservation[reserve_start_hour]': self.start_hour_var.get(),
            'reservation[reserve_start_min]': self.start_min_var.get(),
            'reservation[reserve_start_meridian]': self.start_meridian_var.get(),
            'reservation[reserve_end_date]': self.end_date_var.get(),
            'reservation[reserve_end_hour]': self.end_hour_var.get(),
            'reservation[reserve_end_min]': self.end_min_var.get(),
            'reservation[reserve_end_meridian]': self.end_meridian_var.get(),
            'reservation[duration_mins]_display': self.duration_display_var.get(),
            'reservation[duration_mins]': str(total_minutes),
        }

        self.schedule_button.config(state=tk.DISABLED)
        self.update_status(f"Status: Scheduled for {schedule_dt.strftime('%Y-%m-%d %H:%M:%S')}. Waiting...")

        task_thread = threading.Thread(
            target=self._execute_task,
            args=(delay, instrument, cookies, auth_token, reservation_details), daemon=True
        )
        task_thread.start()

    def _execute_task(self, delay, instrument, cookies, auth_token, reservation_details):
        time.sleep(delay)

        self.root.after(0, self.update_status, f"Status: Executing for {instrument}... Check log.")
        self.root.after(0, self.log_window.deiconify)
        self.root.after(0, lambda: self.log_to_gui(f"--- Starting Reservation Attempts for {instrument} ---\n"))

        start_time = time.time()
        success = False
        while time.time() - start_time < 60:
            response_html = create_order(instrument, cookies, auth_token, reservation_details)

            timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
            log_entry = f"\n{'=' * 80}\nATTEMPT TIMESTAMP: {timestamp}\n{'=' * 80}\n"

            if response_html:
                soup = BeautifulSoup(response_html, 'html.parser')
                log_entry += soup.prettify()
                if not soup.find('div', class_='errorExplanation222222'):
                    self.root.after(0, self.update_status, "Status: Success! Reservation created.")
                    self.root.after(0, lambda: messagebox.showinfo("Success", "Reservation created successfully!"))
                    self.root.after(0, lambda: self.log_to_gui("\n\n--- RESERVATION SUCCESSFUL ---"))
                    success = True
            else:
                log_entry += "--- [INFO] No response from server or network error ---"

            self.root.after(0, self.log_to_gui, log_entry)
            if success: break
            time.sleep(0.2)

        if not success:
            self.root.after(0, self.update_status, "Status: Finished. 60-second window passed.")
            self.root.after(0, lambda: self.log_to_gui("\n\n--- 60-SECOND EXECUTION WINDOW FINISHED ---"))

        self.root.after(0, lambda: self.schedule_button.config(state=tk.NORMAL))

    def create_widgets(self):
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # --- Instrument Selection Frame ---
        inst_frame = ttk.LabelFrame(main_frame, text="Select Instrument", padding=(10, 5))
        inst_frame.grid(row=0, column=0, columnspan=7, sticky=(tk.W, tk.E), pady=(0, 10))
        ttk.Radiobutton(inst_frame, text="fNIRS1", variable=self.instrument_var, value="fNIRS1",
                        command=self._update_title).pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(inst_frame, text="fNIRS2", variable=self.instrument_var, value="fNIRS2",
                        command=self._update_title).pack(side=tk.LEFT, padx=5)

        # --- Main content frame ---
        content_frame = ttk.Frame(main_frame)
        content_frame.grid(row=1, column=0, columnspan=7)

        ttk.Label(content_frame, text="Start Time", font=('Helvetica', 10, 'bold')).grid(row=0, column=0, sticky=tk.W,
                                                                                         pady=2)
        ttk.Entry(content_frame, textvariable=self.start_date_var, width=15).grid(row=0, column=1)
        ttk.Combobox(content_frame, textvariable=self.start_hour_var, values=list(range(1, 13)), width=4).grid(row=0,
                                                                                                               column=2,
                                                                                                               padx=5)
        ttk.Combobox(content_frame, textvariable=self.start_min_var, values=[f"{i:02d}" for i in range(0, 60, 15)],
                     width=4).grid(row=0, column=3)
        ttk.Combobox(content_frame, textvariable=self.start_meridian_var, values=["AM", "PM"], width=4).grid(row=0,
                                                                                                             column=4,
                                                                                                             padx=5)

        ttk.Label(content_frame, text="End Time", font=('Helvetica', 10, 'bold')).grid(row=1, column=0, sticky=tk.W,
                                                                                       pady=2)
        ttk.Entry(content_frame, textvariable=self.end_date_var, width=15).grid(row=1, column=1)
        ttk.Combobox(content_frame, textvariable=self.end_hour_var, values=list(range(1, 13)), width=4).grid(row=1,
                                                                                                             column=2,
                                                                                                             padx=5)
        ttk.Combobox(content_frame, textvariable=self.end_min_var, values=[f"{i:02d}" for i in range(0, 60, 15)],
                     width=4).grid(row=1, column=3)
        ttk.Combobox(content_frame, textvariable=self.end_meridian_var, values=["AM", "PM"], width=4).grid(row=1,
                                                                                                           column=4,
                                                                                                           padx=5)

        ttk.Label(content_frame, text="Duration", font=('Helvetica', 10, 'bold')).grid(row=0, column=5, padx=(20, 0),
                                                                                       sticky=tk.S)
        ttk.Entry(content_frame, textvariable=self.duration_display_var, width=12).grid(row=0, column=6, padx=5,
                                                                                        sticky=tk.S)
        ttk.Label(content_frame, text='Enter "total minutes" or "hours:minutes"').grid(row=1, column=5, columnspan=2,
                                                                                       padx=(20, 0), sticky=tk.N,
                                                                                       pady=(0, 10))

        ttk.Label(content_frame, text="Cookie", font=('Helvetica', 10, 'bold')).grid(row=2, column=0, sticky=tk.W,
                                                                                     pady=5)
        ttk.Entry(content_frame, textvariable=self.cookie_var, width=90).grid(row=2, column=1, columnspan=6,
                                                                              sticky=tk.W, padx=5)

        ttk.Label(content_frame, text="Authenticity Token", font=('Helvetica', 10, 'bold')).grid(row=3, column=0,
                                                                                                 sticky=tk.W, pady=5)
        ttk.Entry(content_frame, textvariable=self.auth_token_var, width=90).grid(row=3, column=1, columnspan=6,
                                                                                  sticky=tk.W, padx=5)

        ttk.Label(content_frame, text="Schedule Time (HH:MM:SS)", font=('Helvetica', 10, 'bold')).grid(row=4, column=0,
                                                                                                       sticky=tk.W,
                                                                                                       pady=10)
        ttk.Entry(content_frame, textvariable=self.schedule_time_var, width=15).grid(row=4, column=1, sticky=tk.W)

        self.schedule_button = ttk.Button(content_frame, text="Schedule Reservation", command=self.schedule_reservation)
        self.schedule_button.grid(row=5, column=5, columnspan=2, sticky=tk.E, pady=10)

        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.grid(row=1, column=0, sticky=(tk.W, tk.E))


if __name__ == '__main__':
    root = tk.Tk()
    app = ReservationApp(root)
    root.mainloop()

sticky=tk.E, pady=10)

        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.grid(row=1, column=0, sticky=(tk.W, tk.E))


if __name__ == '__main__':
    root = tk.Tk()
    app = ReservationApp(root)
    root.mainloop()
相关推荐
泡茶喝茶写代码12 分钟前
量化数据进阶:多维实战篇(第 11 篇):历史涨跌停价:涨跌停序列与止损线测算
java·python·股票数据api·股票数据·股票数据api接口·股票api数据接口·股票量化数据api
ikun_文1 小时前
Django开启跨域请求
python·pycharm·django
外收内放1 小时前
Python与AI应用(项目开发实战:AI智能伴侣第三版)
python·学习·ai编程
别动我齐刘海1 小时前
ROS2 Jazzy + C++ 实战路线——进阶学习3
c++·人工智能·vscode·python·算法·机器学习·机器人
Logintern091 小时前
“以一致的顺序获取锁“是预防死锁最核心、最有效的手段
python·postgresql·锁机制
打工仔折腾 AI1 小时前
Prometheus 告警推钉钉:从单群 Webhook 到跨网络 Alertmanager 实战
人工智能·后端·python·性能优化
Ivanqhz1 小时前
Ping-Pong 双缓冲
开发语言·人工智能·python·深度学习·mlir
三十岁老牛再出发1 小时前
9月18日总结
python·深度学习·机器学习
就叫你天选之人啦2 小时前
安装torch+vllm+flash_attn的prompt
人工智能·pytorch·python
wuyk5552 小时前
21.计数排序:不用比较的“空间换时间”排序算法
python·算法·排序算法