@@ -4,17 +4,164 @@ jupytext:
44 extension : .md
55 format_name : myst
66 format_version : 0.13
7- jupytext_version : 1.10.3
7+ jupytext_version : 1.15.1
88kernelspec :
9- display_name : Python 3
9+ display_name : Python 3 (ipykernel)
1010 language : python
1111 name : python3
1212---
1313
14- # Badge Procedures
14+ # Badge Deadlines and Procedures
1515
1616This page includes more visual versions of the information on the badge page. You should read both, but this one is often more helpful, because some of the processes take a lot of words to explain and make more sense with a diagram for a lot of people.
1717
18+ ``` {code-cell} ipython3
19+ %matplotlib inline
20+ import os
21+ from datetime import date,timedelta
22+ import calendar
23+ import pandas as pd
24+ import numpy as np
25+ import seaborn as sns
26+ from myst_nb import glue
27+ # style note: when I wrote this code, it was not all one cell. I merged the cells
28+ # for display on the course website, since Python is not the main outcome of this course
29+
30+ # semester settings
31+ first_day = date(2023,9,5)
32+ last_day = date(2023,12,12)
33+
34+ no_class_ranges = [(date(2023,11,23),date(2023,11,26)),
35+ (date(2023,11,13)),
36+ (date(2023,10,10))]
37+
38+
39+ meeting_days =[1,3] # datetime has 0=Monday
40+ spring_break = (date(2023,3,11),date(2023,3,19))
41+ penalty_free_end = date(2023, 9, 28)
42+
43+
44+ def day_off(cur_date,skip_range_list):
45+ '''
46+ is the current date a day off?
47+
48+ Parameters
49+ ----------
50+ cur_date : datetime.date
51+ date to check
52+ skip_range_list : list of datetime.date objects or 2-tuples of datetime.date
53+ dates where there is no class, either single dates or ranges specified by a tuple
54+
55+ Returns
56+ -------
57+ day_is_off : bool
58+ True if the day is off, False if the day has class
59+ '''
60+ # default to not a day off
61+ day_is_off=False
62+ #
63+ for skip_range in skip_range_list:
64+ if type(skip_range) == tuple:
65+ # if any of the conditions are true that increments and it will never go down, flase=0, true=1
66+ day_is_off += skip_range[0]<=cur_date<=skip_range[1]
67+ else:
68+ day_is_off += skip_range == cur_date
69+ #
70+ return day_is_off
71+
72+
73+ # enumerate weeks
74+ meeting_days =[1,3] # spring
75+ mtg_delta = timedelta(meeting_days[1]-meeting_days[0])
76+ week_delta = timedelta(7)
77+
78+ during_sb = lambda d: spring_break[0]<d<spring_break[1]
79+
80+ possible = [(first_day+week_delta*w, first_day+mtg_delta+week_delta*w) for w in range(weeks)]
81+ weekly_meetings = [[c1,c2] for c1,c2 in possible if not(day_off(c1,no_class_ranges))]
82+ meetings = [m for w in weekly_meetings for m in w]
83+ meetings_string = [m.isoformat() for m in meetings]
84+ weekly_meetings
85+
86+
87+ # possible = [(first_day+week_delta*w, first_day+mtg_delta+week_delta*w) for w in range(weeks)]
88+ # weekly_meetings = [[c1,c2] for c1,c2 in possible if not(during_sb(c1))]
89+ meetings = [m for w in weekly_meetings for m in w if not(m in skips)]
90+
91+
92+ # build a table for the dates
93+ badge_types = ['experience', 'review', 'practice']
94+ target_cols = ['review_target','practice_target']
95+ df_cols = badge_types + target_cols
96+ badge_target_df = pd.DataFrame(index=meetings, data=[['future']*len(df_cols)]*len(meetings),
97+ columns=df_cols).reset_index().rename(
98+ columns={'index': 'date'})
99+ # set relative dates
100+ today = date.today()
101+ start_deadline = date.today() - timedelta(7)
102+ complete_deadline = date.today() - timedelta(14)
103+
104+ # mark eligible experience badges
105+ badge_target_df['experience'][badge_target_df['date'] <= today] = 'eligible'
106+ # mark targets, cascading from most recent to oldest to not have to check < and >
107+ badge_target_df['review_target'][badge_target_df['date'] <= today] = 'active'
108+ badge_target_df['practice_target'][badge_target_df['date'] <= today] = 'active'
109+ badge_target_df['review_target'][badge_target_df['date']
110+ <= start_deadline] = 'started'
111+ badge_target_df['practice_target'][badge_target_df['date']
112+ <= start_deadline] = 'started'
113+ badge_target_df['review_target'][badge_target_df['date']
114+ <= complete_deadline] = 'completed'
115+ badge_target_df['practice_target'][badge_target_df['date']
116+ <= complete_deadline] = 'completed'
117+ # mark enforced deadlines
118+ badge_target_df['review'][badge_target_df['date'] <= today] = 'active'
119+ badge_target_df['practice'][badge_target_df['date'] <= today] = 'active'
120+ badge_target_df['review'][badge_target_df['date']
121+ <= start_deadline] = 'started'
122+ badge_target_df['practice'][badge_target_df['date']
123+ <= start_deadline] = 'started'
124+ badge_target_df['review'][badge_target_df['date']
125+ <= complete_deadline] = 'completed'
126+ badge_target_df['practice'][badge_target_df['date']
127+ <= complete_deadline] = 'completed'
128+ badge_target_df['review'][badge_target_df['date']
129+ <= penalty_free_end] = 'penalty free'
130+ badge_target_df['practice'][badge_target_df['date']
131+ <= penalty_free_end] = 'penalty free'
132+
133+ # convert to numbers and set dates as index for heatmap compatibility
134+ status_numbers_hm = {status:i+1 for i,status in enumerate(['future','eligible','active','penalty free','started','completed'])}
135+ badge_target_df_hm = badge_target_df.replace(status_numbers_hm).set_index('date')
136+
137+ # set column names to shorter ones to fit better
138+ badge_target_df_hm = badge_target_df_hm.rename(columns={'review':'review(e)','practice':'practice(e)',
139+ 'review_target':'review(t)','practice_target':'practice(t)',})
140+ # build a custom color bar
141+ n_statuses = len(status_numbers_hm.keys())
142+ manual_palette = [sns.color_palette("pastel", 10)[7],
143+ sns.color_palette("colorblind", 10)[2],
144+ sns.color_palette("muted", 10)[2],
145+ sns.color_palette("colorblind", 10)[9],
146+ sns.color_palette("colorblind", 10)[8],
147+ sns.color_palette("colorblind", 10)[3]]
148+ # generate the figure, with the colorbar and spacing
149+ ax = sns.heatmap(badge_target_df_hm,cmap=manual_palette,linewidths=1)
150+ # mote titles from bottom tot op
151+ ax.xaxis.tick_top()
152+ # pull the colorbar object for handling
153+ colorbar = ax.collections[0].colorbar
154+ # fix the location fo the labels on the colorbar
155+ r = colorbar.vmax - colorbar.vmin
156+ colorbar.set_ticks([colorbar.vmin + r / n_statuses * (0.5 + i) for i in range(n_statuses)])
157+ colorbar.set_ticklabels(list(status_numbers_hm.keys()))
158+ # add a title
159+ today_string = today.isoformat()
160+ glue('today',today_string,display=False)
161+ glue('today_notdisplayed',"not today",display=False)
162+ ax.set_title('Badge Status as of '+ today_string);
163+ ```
164+
18165## Prepare work and Experience Badges Process
19166
20167``` {warning}
@@ -67,6 +214,7 @@ gitGraph
67214
68215Where the "approved" tag represents and approving reivew on the PR.
69216
217+ +++
70218
71219## Review and Practice Badge
72220
@@ -322,4 +470,8 @@ flowchart TD
322470 rev --> approved[Dr. Brown approves] --> merge[Merge the PR]
323471 merge --o earned
324472
325- ```
473+ ```
474+
475+ ``` {code-cell} ipython3
476+
477+ ```
0 commit comments