def update_faculty_fte_chart(dept): resp = table.query( KeyConditionExpression='PK = :pk AND SK BETWEEN :lower AND :upper', ExpressionAttributeValues={ ':pk': f'DEPT#{dept}', ':lower': 'DATA#FACULTY_DATA#2005', ':upper': f'DATA#FACULTY_DATA#{int(MAX_FISCAL_YEAR) + 1}$', }, ProjectionExpression='fte, ten_stat', ScanIndexForward=True, ) data = resp['Items'] chart_data = [] x_axis = make_academic_year_range(0, MAX_YEAR_ID) # Categories determine filtering for the y-axis and obtaining a color category_names = { 'Tenured': 'Tenured', 'NTBOT': 'NTBOT', 'NTBOT-professor-term': 'Term Asst. Prof', 'Lecturers': 'Lecturers', 'Other Full-Time': 'Other Full-Time', 'Adjunct': 'Adjunct', } for cat in category_names.keys(): y_axis = [ item.get('fte') for item in data if item.get('ten_stat') == cat ] chart_data.append( go.Bar( name=category_names.get(cat), x=x_axis, y=y_axis, text=[f' {round(float(i))} ' for i in y_axis ], # pad with spaces to prevent labels from rotating textposition='inside', hovertext=[f'{cat}: {i}' for i in y_axis], hoverinfo='text', marker=dict( color=faculty_colors.get(cat), line=dict(color='floralwhite', width=1), ))) chart_layout = go.Layout( barmode='stack', xaxis=axes(), yaxis=axes(title='FTE for Faculty/Person Count for Adjuncts', ), legend={'traceorder': 'normal'}, margin=margin(l=55), ) return {'data': chart_data, 'layout': chart_layout}
def update_student_ug_chart(dept): resp = table.query( KeyConditionExpression='PK = :pk AND SK BETWEEN :lower AND :upper', ExpressionAttributeValues={ ':pk': f'DEPT#{dept}', ':lower': 'DATA#STUDENTS#UG#2005', ':upper': f'DATA#STUDENTS#UG#{int(MAX_FISCAL_YEAR) + 1}$', }, ScanIndexForward=True, ) data = resp['Items'] chart_data = [] x_axis = make_academic_year_range(0, MAX_YEAR_ID) # Categories determine filtering for the y-axis and obtaining a color categories = { 'maj': 'Majors', 'conc': 'Concentrations', 'intdmaj': 'Interdepartmental Majors', 'min': 'Minors' } for cat in categories.keys(): y_axis = [item.get(cat) for item in data] chart_data.append( go.Bar( name=categories.get(cat), x=x_axis, y=y_axis, text=[i if len(i) > 1 else f' {i} ' for i in y_axis ], # pad single-digit numbers to prevent rotation textposition='inside', hovertext=[f'{categories.get(cat)}: {i}' for i in y_axis], hoverinfo='text', marker=dict( color=students_ug_colors.get(cat), line=dict(color='floralwhite', width=1), ))) chart_layout = go.Layout( barmode='stack', xaxis=axes(), yaxis=axes(title='Number of Students', ), legend={'traceorder': 'normal'}, margin=margin(l=55), ) return {'data': chart_data, 'layout': chart_layout}
def update_classes_bar_chart(dept): # Use ExpressionAttributeNames because 'count' is a restricted keyword for ProjectionExpression resp = table.query( KeyConditionExpression='PK = :pk AND SK BETWEEN :lower AND :upper', ExpressionAttributeValues={ ':pk': f'DEPT#{dept}', ':lower': 'DATA#AGG#CLASSES#2008', ':upper': f'DATA#AGG#CLASSES#{int(MAX_FISCAL_YEAR) + 1}$', }, ProjectionExpression='#c, ten_stat', ExpressionAttributeNames={'#c': 'count'}, ScanIndexForward=True, ) data = resp['Items'] chart_data = [] x_axis = make_academic_year_range(3, MAX_YEAR_ID) for data_cat, chart_cat in tenure_categories.items(): y_axis = [ item.get('count') for item in data if item.get('ten_stat') == data_cat ] chart_data.append( go.Bar(name=chart_cat, x=x_axis, y=y_axis, text=[f'{round(float(i)):,}' for i in y_axis], textposition='inside', hovertext=[ f'{chart_cat}: {round(float(i)):,}' for i in y_axis ], hoverinfo='text', marker=dict( color=classes_colors.get(data_cat), line=dict(color='floralwhite', width=1), ))) chart_layout = go.Layout( barmode='stack', xaxis=axes(), yaxis=axes(title='Number of Classes', ), legend={'traceorder': 'normal'}, margin=margin(l=70), ) return {'data': chart_data, 'layout': chart_layout}
def update_student_phd_chart(dept): resp = table.query( KeyConditionExpression='PK = :pk AND SK BETWEEN :lower AND :upper', ExpressionAttributeValues={ ':pk': f'DEPT#{dept}', ':lower': 'DATA#STUDENTS#PHD#2005', ':upper': f'DATA#STUDENTS#PHD#{int(MAX_FISCAL_YEAR) + 1}$', }, ScanIndexForward=True, ) data = resp['Items'] if is_blank_grad(data): return [], {'display': 'none'} chart_data = [] x_axis = make_academic_year_range(0, MAX_YEAR_ID) # Categories determine filtering for the y-axis and obtaining a color categories = { 'cohort': 'Entering Cohort<br>(starting 2009/10)', 'existing': 'Existing Students', } for cat in categories.keys(): y_axis = [item.get(cat) for item in data] chart_data.append( go.Bar( name=categories.get(cat), x=x_axis, y=y_axis, text=[i if len(i) > 1 else f' {i} ' for i in y_axis ], # pad single-digit numbers to prevent rotation textposition='inside', hovertext=[f'{categories.get(cat)}: {i}' for i in y_axis], hoverinfo='text', marker=dict( color=students_grad_colors.get(cat), line=dict(color='floralwhite', width=1), ))) if dept not in ['AS', 'HUM', 'NS', 'SS']: # Do not add selectivity/yield for aggregates for cat in ('selectivity', 'yield'): y_axis_selectivity = [ round(float(item.get(cat)) * 100) if item.get(cat) is not None else None for item in data ] hover_labels = [ f'{i}%' if i is not None else None for i in y_axis_selectivity ] text_labels = make_text_labels(hover_labels) chart_data.append( go.Scatter( name=f'{cat.title()}', x=x_axis, y=y_axis_selectivity, mode='lines+markers+text', text=text_labels, textposition='top center', textfont=dict(color=students_grad_colors.get(cat), ), hovertext=hover_labels, hoverinfo='text', marker=dict(color=students_grad_colors.get(cat), ), yaxis='y2')) chart_layout = go.Layout( barmode='stack', xaxis=axes(), yaxis=axes(title='Number of Students', ), yaxis2=axes( title='% Selectivity or Yield', overlaying='y', side='right', rangemode='tozero', showgrid=False, ), legend={ 'traceorder': 'normal', 'x': 1.05 }, # By default x is 1.02 which will make it overlap with the 2nd y-axis margin=margin(l=55), ) else: chart_layout = go.Layout( barmode='stack', xaxis=axes(), yaxis=axes(title='Number of Students', ), legend={ 'traceorder': 'normal', 'x': 1.05 }, # By default x is 1.02 which will make it overlap with the 2nd y-axis margin=margin(l=55), ) return { 'data': chart_data, 'layout': chart_layout }, { 'display': 'inline' }
def update_student_sps_chart(dept): resp = table.query( KeyConditionExpression='PK = :pk AND SK BETWEEN :lower AND :upper', ExpressionAttributeValues={ ':pk': f'DEPT#{dept}', ':lower': 'DATA#STUDENTS#SPS#2005', ':upper': f'DATA#STUDENTS#SPS#{int(MAX_FISCAL_YEAR) + 1}$', }, ScanIndexForward=True, ) data = resp['Items'] if is_blank_grad(data): return [], {'display': 'none'} chart_data = [] x_axis = make_academic_year_range(0, MAX_YEAR_ID) # Categories determine filtering for the y-axis and obtaining a color categories = { 'cohort': 'Entering Cohort<br>(starting 2009/10)', 'existing': 'Existing Students', } for cat in categories.keys(): y_axis = [item.get(cat) for item in data] chart_data.append( go.Bar( name=categories.get(cat), x=x_axis, y=y_axis, text=[i if len(i) > 1 else f' {i} ' for i in y_axis ], # pad single-digit numbers to prevent rotation textposition='inside', hovertext=[f'{categories.get(cat)}: {i}' for i in y_axis], hoverinfo='text', marker=dict( color=students_grad_colors.get(cat), line=dict(color='floralwhite', width=1), ))) chart_layout = go.Layout( barmode='stack', xaxis=axes(), yaxis=axes(title='Number of Students', ), legend={ 'traceorder': 'normal', 'x': 1.05 }, # By default x is 1.02 which will make it overlap with the 2nd y-axis margin=margin(l=55), ) return { 'data': chart_data, 'layout': chart_layout }, { 'display': 'inline' }
def update_faculty_demo_chart(dept): resp = table.query( KeyConditionExpression='PK = :pk AND SK BETWEEN :lower AND :upper', ExpressionAttributeValues={ ':pk': f'DEPT#{dept}', ':lower': 'DATA#FACULTY_DATA#2005', ':upper': f'DATA#FACULTY_DATA#{int(MAX_FISCAL_YEAR) + 1}$', }, FilterExpression=Attr('ten_stat').eq('Tenured') | Attr('ten_stat').eq('NTBOT'), ProjectionExpression='fte, ten_stat, percent_fem, percent_urm', ScanIndexForward=True, ) data = resp['Items'] chart_data = [] x_axis = make_academic_year_range(0, MAX_YEAR_ID) # Construct charts without a loop to preserve y values for further calculation y_axis_bar_t = [ item.get('fte') for item in data if item.get('ten_stat') == 'Tenured' ] chart_data.append( go.Bar( name='Tenured', x=x_axis, y=y_axis_bar_t, text=[f' {round(float(i))} ' for i in y_axis_bar_t ], # pad with spaces to prevent labels from rotating textposition='inside', hovertext=[f'Tenured: {i}' for i in y_axis_bar_t], hoverinfo='text', marker=dict( color=faculty_colors.get('Tenured'), line=dict(color='floralwhite', width=1), ))) y_axis_bar_nt = [ item.get('fte') for item in data if item.get('ten_stat') == 'NTBOT' ] chart_data.append( go.Bar( name='NTBOT', x=x_axis, y=y_axis_bar_nt, text=[f' {round(float(i))} ' for i in y_axis_bar_nt ], # pad with spaces to prevent labels from rotating textposition='inside', hovertext=[f'NTBOT: {i}' for i in y_axis_bar_nt], hoverinfo='text', marker=dict( color=faculty_colors.get('NTBOT'), line=dict(color='floralwhite', width=1), ))) # LINE PLOTS y_axis_line_t = [ round(float(item.get('percent_fem')) * 100) if item.get('percent_fem') is not None else None for item in data if item.get('ten_stat') == 'Tenured' ] hover_labels = [ f'{i}%' if i is not None else None for i in y_axis_line_t ] text_labels = make_text_labels(hover_labels) chart_data.append( go.Scatter(name='% Tenured Female', x=x_axis, y=y_axis_line_t, mode='lines+markers+text', text=text_labels, textposition='top center', textfont=dict(color=colors.get('orange2'), ), hovertext=hover_labels, hoverinfo='text', marker=dict(color=colors.get('orange2'), ), yaxis='y2')) y_axis_line_nt = [ round(float(item.get('percent_fem')) * 100) if item.get('percent_fem') is not None else None for item in data if item.get('ten_stat') == 'NTBOT' ] hover_labels = [ f'{i}%' if i is not None else None for i in y_axis_line_nt ] text_labels = make_text_labels(hover_labels) chart_data.append( go.Scatter(name='% NTBOT Female', x=x_axis, y=y_axis_line_nt, mode='lines+markers+text', text=text_labels, textposition='top center', textfont=dict(color=colors.get('red1'), ), hovertext=hover_labels, hoverinfo='text', marker=dict(color=colors.get('red1'), ), yaxis='y2')) # URM line calculation # (Tenured FTE * Tenured % URM + NTBOT FTE * NTBOT% URM) / (Tenured FTE + NTBOT FTE) urm_t = [ float(item.get('percent_urm')) if item.get('percent_urm') is not None else None for item in data if item.get('ten_stat') == 'Tenured' ] urm_nt = [ float(item.get('percent_urm')) if item.get('percent_urm') is not None else None for item in data if item.get('ten_stat') == 'NTBOT' ] y_axis_line_urm = [] for t_fte, nt_fte, t_urm, nt_urm in zip(y_axis_bar_t, y_axis_bar_nt, urm_t, urm_nt): calc = None if t_urm is not None and nt_urm is not None: calc = (float(t_fte) * t_urm + float(nt_fte) * nt_urm) / ( float(t_fte) + float(nt_fte)) elif t_urm is not None and nt_urm is None: calc = (float(t_fte) * t_urm + float(nt_fte) * 0) / (float(t_fte) + float(nt_fte)) elif nt_urm is not None and t_urm is None: calc = (float(t_fte) * 0 + float(nt_fte) * nt_urm) / ( float(t_fte) + float(nt_fte)) if calc is None: y_axis_line_urm.append(None) else: y_axis_line_urm.append(round(calc * 100)) hover_labels = [ f'{i}%' if i is not None else None for i in y_axis_line_urm ] text_labels = make_text_labels(hover_labels) chart_data.append( go.Scatter(name='% NTBOT and Tenured URM', x=x_axis, y=y_axis_line_urm, mode='lines+markers+text', text=text_labels, textposition='top center', textfont=dict(color=colors.get('teal1'), ), hovertext=hover_labels, hoverinfo='text', marker=dict(color=colors.get('teal1'), ), yaxis='y2')) chart_layout = go.Layout( barmode='stack', xaxis=axes(), yaxis=axes(title='FTE', ), yaxis2=axes( title='% FTE', overlaying='y', side='right', rangemode='tozero', showgrid=False, ), legend={ 'traceorder': 'normal', 'x': 1.05 }, # By default x is 1.02 which will make it overlap with the 2nd y-axis margin=margin(), ) return {'data': chart_data, 'layout': chart_layout}