Although the current schedule viewing for Chongqing University of Posts and Telecommunications can be done through the Mobile Chongyou app and the We Chongyou WeChat mini program, neither of these applications can notify users in advance when there are classes. Therefore, it would be better to provide schedule data through the interfaces of these two applications and then convert it into a universal iCalendar format for calendar import. This way, users can experience system-level calendar notifications.
Previously, there was a Python version of CQUPT-ics written by a senior from Chongyou, but currently, the interfaces of these two data sources have changed and can no longer be used properly.
Therefore, I plan to write a new project to achieve the above goal. Following the idea of the previous senior's project, I will do a rewrite RIIR (Rewrite It In Rust).
New project address: https://github.com/jizizr/cqupt-ics-rs The project source code is here.
Usage address: https://cqupt.op.wiki/ To use this project, click here.
It basically implements the functionality of the previous Python version, and additionally, it also implements automatic course adjustment for rescheduled classes. This avoids receiving class notifications during holidays and forgetting that there are classes on days that have been rescheduled (the official Chongyou schedule will not change according to holidays, and you need to manually check the rescheduling arrangements).
Of course, relying solely on concatenating subscription links is not user-friendly; a front-end user interface (AI generated) should make it easier to use this project. The school will close the schedule query at night, so it is normal that the schedule cannot be checked from around 0 to 7 AM
PS: The one-click import feature has only been implemented for Android and Apple systems. Other systems should manually copy the link and add it to a calendar that supports ICS subscriptions.
Originally, I planned to use webcal:// to redirect to calendar addition. Theoretically, the webcal:// protocol header redirection is implemented by most systems, but due to the different calendar software provided on systems other than Apple (macOS, iOS), there are significant differences and often strange issues arise. Therefore, it is only provided for Apple, and Android uses intent:// to redirect to the system calendar. Thus, it has only been implemented for Apple systems and Android.
If there are better implementations, feel free to submit a PR, and if there are bugs, please submit an issue.
Finally, let's take a look at the import effect screenshot.
All classes were removed during the National Day (holiday), and there are corresponding rescheduled classes during the National Day (class).
The effect on the phone.
To add a new provider source, you only need to implement a trait (very simple).
#[async_trait]
pub trait Provider: Send + Sync {
/// Token type for this provider
type Token: Send + Sync + Serialize + DeserializeOwned;
type ContextType: Send + Sync;
/// Name of the provider
fn name(&self) -> &str;
/// Description of the provider
fn description(&self) -> &str;
/// Get timezone for this provider
///
/// Returns the timezone used by this provider for time calculations.
/// This is used to ensure consistent timezone handling across all
/// provider operations.
/// Timezone of the provider
fn timezone(&self) -> FixedOffset;
/// Authenticate and get token
/// Method to get the token
async fn authenticate<'a, 'b>(
&'a self,
context: ParamContext<'b, Self::ContextType>,
request: &CourseRequest,
) -> Result<Self::Token>;
/// Validate existing token
/// Validate if the token is valid
async fn validate_token(&self, token: &Self::Token) -> Result<bool>;
/// Refresh token
/// Method to refresh the token (if the provider does not exist, just return Err)
async fn refresh_token(&self, token: &Self::Token) -> Result<Self::Token>;
/// Get courses using token
/// request.semester should be Some before calling this method
/// If you use crate::providers::Wrapper, it will ensure semester is Some
/// Method to get course data
async fn get_courses<'a, 'b>(
&'a self,
context: ParamContext<'b, Self::ContextType>,
request: &mut CourseRequest,
token: &Self::Token,
) -> Result<CourseResponse>;
/// Get semester start date
/// This is called if request.semester is None before get_courses
/// If you use crate::providers::Wrapper, it will call this method automatically if request.semester is None
/// You can use the context to store intermediate data if needed
/// Method to get the semester start date
async fn get_semester_start<'a, 'b>(
&'a self,
context: ParamContext<'b, Self::ContextType>,
request: &mut CourseRequest,
token: &Self::Token,
) -> Result<chrono::DateTime<FixedOffset>>;
/// Token TTL
/// Returns the token's validity period for cache control
fn token_ttl(&self) -> Duration {
Duration::from_secs(3600 * 24) // 24 hours default
}
}