Skip to content

Commit 61ff0e2

Browse files
authored
Update accessors-term.r
added set_term and clean_class_days functions
1 parent 45d09ca commit 61ff0e2

File tree

1 file changed

+115
-66
lines changed

1 file changed

+115
-66
lines changed

R/accessors-term.r

Lines changed: 115 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,124 @@
1-
#' Get the fiscal term of a date-time
1+
#' Get create a term data frame
22
#'
3-
#' Quarters divide the year into fourths. Trimesters divide the year into thirds. Semesters divide the year into halfs.
4-
#'
5-
#' @param x a date-time object of class POSIXct, POSIXlt, Date, chron, yearmon, yearqtr,
6-
#' zoo, zooreg, timeDate, xts, its, ti, jul, timeSeries, fts or anything else that can
7-
#' be converted with as.POSIXlt
8-
#' @param term the way you are dividing the year, be it semesters, trimesters, or quarters.
9-
#' @param type the format to be returned for the quarter. Can be one one of "quarter" -
10-
#' return numeric quarter (default), "year.quarter" return the ending year and quarter
11-
#' as a number of the form year.quarter, "date_first" or "date_last" - return the date
12-
#' at the quarter's start or end, "year_start/end" - return a full description of the
13-
#' quarter as a string which includes the start and end of the year
14-
#' (ex. "2020/21 Q1").
15-
#' @param fiscal_start numeric indicating the starting month of a fiscal year.
3+
#' @param start_date The first day of the term in year month day format.
4+
#' @param weeks The length of the term in weeks. Must be a singular numeric value,
5+
#' automates to 10
6+
#' @param skip list of dates for which there may not be school, and pushes the end of the term back
7+
#' @param holidays list of dates indicating major holidays, marks it in the final
8+
#' data frame as a holiday, but does not add length to the term.
9+
#' @param class_days To indicate which days of the week there are class.
10+
#' For example, if Tuesday and Friday are class days the input must be written
11+
#' like c(2, 5), c('Tue', 'Fri'), c('Tu', 'F'), c('Tuesday', 'Friday'),
12+
#' or c('Tue', 'Friday'), etc
1613
#' @param holidays list of dates indicating major holidays
17-
#' @param split_count should you want to split up the year in your own number you can
18-
#' use an "other" and theninput your own division count as a numeric to split_count
14+
#' @param exams list of exam dates
15+
16+
17+
set_term <- function(start_date= "2025-01-01",
18+
weeks = 10,
19+
skip= NULL,
20+
holidays= NULL,
21+
class_days = NULL,
22+
exams = NULL){
23+
24+
class_days <- clean_class_days(class_days)
25+
total_skip <- 0
1926

20-
term <- function(x, term = "quarter", type = "term", fiscal_start = 1, holidays = NULL, split_count = NULL){
21-
if (length(fiscal_start) > 1) {
22-
stop("`fiscal_start` must be a singleton", call. = FALSE)
27+
if (is.null(weeks)||!is.numeric(weeks)) {
28+
stop("weeks parameter must contain a numeric value")
2329
}
24-
fs <- (fiscal_start - 1) %% 12
25-
shifted <- seq(fs, 11 + fs) %% 12 + 1
26-
m <- month(x)
27-
s <- match(m, shifted)
28-
29-
num_divisions <- switch(term,
30-
"quarter" = 4,
31-
"trimester" = 3,
32-
"semester" = 2,
33-
"other" = split_count,
34-
stop("Unsupported type: ", term))
35-
36-
divisions <- rep(1:num_divisions, each = 12 / num_divisions)
37-
q <- divisions[s]
38-
39-
if (is.logical(type)) {
40-
type <- if (type) "year.term" else "term"
30+
31+
if (is.null(start_date)) {
32+
stop("start_date parameter must contain a date in year-month-day form")
4133
}
42-
43-
44-
if (!is.null(holidays)) {
45-
is_holiday <- as.Date(x) %in% as.Date(holidays)
46-
return(list(division = current_division, holiday = is_holiday))
34+
all_skip_dates <- c()
35+
if (!is.null(skip)) {
36+
if (!all(c("start", "end") %in% names(skip))) {
37+
stop("`skip` data frame must contain `start` and `end` columns.")
38+
}
39+
40+
skip$end <- ymd(skip$'end')
41+
skip$start <- ymd(skip$'start')
42+
for(i in 1:nrow(skip)){
43+
skip_range <- seq(ymd(skip$start[i]),
44+
ymd(skip$end[i]),
45+
by='days')
46+
47+
all_skip_dates <- unique(c(all_skip_dates,
48+
skip_range))
49+
}
50+
total_skip <- length(all_skip_dates)
51+
52+
4753
}
4854

49-
switch(type,
50-
"term" = q,
51-
"year_start/end" = ,
52-
"year.term" = {
53-
nxt_year_months <- if (fs != 0) (fs + 1):12
54-
y = year(x) + (m %in% nxt_year_months)
55-
out = y + (q / 10)
56-
if (type == "year_start/end") {
57-
out = sprintf("%d/%d Q%d", y - 1, y %% 100, q)
58-
}
59-
out
60-
},
61-
"date_first" = ,
62-
"date_last" = {
63-
starting_months <- shifted[seq(1, length(shifted), 3)]
64-
final_years <- year(x) - (starting_months[q] > m)
65-
quarter_starting_dates <-
66-
make_date(year = final_years, month = starting_months[q], day = 1L)
67-
if (type == "date_first") {
68-
quarter_starting_dates
69-
} else if (type == "date_last") {
70-
add_with_rollback(quarter_starting_dates, months(3)) - days(1)
71-
}
72-
},
73-
stop("Unsupported type ", type)
55+
start_date <- ymd(start_date)
56+
total_days <- weeks*7 + total_skip
57+
end_date <- start_date +days(total_days)
58+
date_range <- seq(start_date, end_date, by='days')
59+
60+
term <- data.frame(
61+
date = date_range,
62+
day = weekdays(date_range),
63+
status = "no class",
64+
stringsAsFactors = FALSE,
65+
class = FALSE
7466
)
67+
68+
term$status[term$day %in% class_days] <- "class"
69+
term$status[term$date %in% all_skip_dates] <-"skip"
70+
term$status[term$date %in% ymd(holidays)] <- "holiday"
71+
term$status[term$date %in% ymd(exams)] <- "exam"
72+
term$class[term$status %in% c("class", "exam")] <- TRUE
73+
return(term)
7574
}
75+
76+
77+
clean_class_days <- function(class_days) {
78+
week_days <- c()
79+
valid_days <- c("Monday", "Tuesday", "Wednesday",
80+
"Thursday", "Friday", "Saturday", "Sunday")
81+
short_days <- c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
82+
letter_days <- c("M", "Tu", "W", "Tr", "F", "Sa", "Su")
83+
84+
85+
if (is.null(class_days)) {
86+
return(c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"))
87+
}
88+
89+
for (i in seq_along(class_days)) {
90+
day <- class_days[i]
91+
92+
#numeric input
93+
if (is.numeric(day)) {
94+
if (day >= 1 && day <= 7) {
95+
week_days <- c(week_days, valid_days[day])
96+
} else {
97+
stop("Numeric `class_days` values must be between 1 and 7.")
98+
}
99+
}
100+
#short names like "Mon", "Tue"
101+
else if (day %in% short_days) {
102+
week_days <- c(week_days, valid_days[match(day, short_days)])
103+
}
104+
#single-letter abbreviations like "M", "Tu"
105+
else if (day %in% letter_days) {
106+
week_days <- c(week_days, valid_days[match(day, letter_days)])
107+
}
108+
#full names like "Monday", "Tuesday"
109+
else if (day %in% valid_days) {
110+
week_days <- c(week_days, day)
111+
}
112+
#invalid input
113+
else {
114+
stop("Invalid `class_days` value. Must be a valid day of the week.
115+
For example, if Tuesday, must be written like c(2),
116+
c('Tue'), c('Tu'), or c('Tuesday')")
117+
}
118+
}
119+
120+
return(unique(week_days))
121+
}
122+
123+
#' @rdname set_term
124+
#' @export

0 commit comments

Comments
 (0)