diff --git a/app.R b/app.R new file mode 100644 index 00000000..e17f98ac --- /dev/null +++ b/app.R @@ -0,0 +1,832 @@ +# Data Incite RPI Study Safe App using UI Template +# Uses Shinydashboard as a framework https://rstudio.github.io/shinydashboard/ +# UI Template Created by Arielle Cerini, Brian Hotopp, Haoyu He, and James Young +# Edited by Kara Kniss + +#document for building tagging information... will possibly use later +#source('read_buildingType.R') + +#document for reading in the relevant libraries, data, and cleaning +source('read_wapData.R') + +#LINK TO FEEDBACK SURVEY +survey_url <- tags$a("survey", href = "https://docs.google.com/forms/d/e/1FAIpQLScvNquZHFMPXmzvj2_FGpzjpC7eMmfMEeO4sM_aSqZNhYCBFg/viewform?usp=sf_link", style = 'color: #990000; background-color: #FAFAFA') + +#LINK TO USABILITY STUDY +usability_study_url <- tags$a("usability study", href = "https://forms.gle/tpMfaMv5dBfXSJUH6", style = 'color: #990000; background-color: #FAFAFA') + + +ui <- dashboardPage(skin = "black", title = "RPI StudySafe", + + #HEADER + dashboardHeader(title = tags$div( + class = "title-text", + tags$style(".logo {max-width: 80%;}"), + tags$div(id = "logo_block", tags$img(class="logo", src="Rensselaer.png", id="Rensselear Polytechnic Institute Logo") )), + titleWidth = "340px" + ), + + #TOGGLED SIDEBAR + dashboardSidebar( + width = 320, + sidebarMenu(id = "tabs", collapsed = TRUE, + menuItem("About", tabName = "about"), + menuItem("Map", tabName = "map", selected = TRUE), + menuItem("Find a Place To Study", tabName = "find"), + menuItem('Campus at a Glance', tabName='overview'), + tags$div(HTML("

")), + box(width= 12, collapsible = TRUE, title = "Feedback Forms", style = 'color: grey; background-color: #FAFAFA', solidHeader=TRUE, + tagList("Have any comments? Please follow the link to", + tags$br(), + "this", survey_url, "and let us know if you have any", + tags$br(), + " issues, questions, or suggestions regarding", + tags$br(), + " the StudySafe app."), + tags$br(), tags$br(), + tagList("Help us make the app better! Take a few", + tags$br(), + "minutes to participate in our ",usability_study_url, ".") + ), + uiOutput("side_ctrl") + ) + ), + + #BODY WITH REACTIVE TABS + dashboardBody( + style = "background-color: #FAFAFA; height: 100%; min-height: 100vh;", + shinyjs::useShinyjs(), + tabItems( + + #ABOUT TAB + tabItem(tabName = "about", + uiOutput("about_body"), + tags$div(HTML("

")), + tags$div( + HTML(" +

About RPI StudySafe

+

An Introduction to the Application

+

RPI StudySafe is an app that reveals the usage of Wi-Fi access points and aggregations of wireless users on the campus network at Rensselaer Polytechnic Institute, Troy, New York. + The data can be interacted with in several ways, including selecting certain dates, times, locations, as well as interactive geographical maps. Geographic location, time section, and density of each access point are combined by building. + \n The \"Map\" tab includes a campus map, providing a succinct overview of how campus is behaving. + \n The \"Find a Place to Study\" is intended to be a go-to quicksearch of any building. + \n The \"Campus at a Glance\" tab is intended to be a more in depth search, involving comparison of several locations on campus at once.

+

Goals

+

Using the campus WAP data, we provide students an interactive application to help them make informed decisions about selecting a place to sit and stay for long periods of time on campus. + Students will be able to investigate certain dates,times, and buildings, as well as compare them and investigate simulated data.

+

About its Creators

+

This application was inspired by the work done by the Contact Tracing team in the Institute for Data Exploration and Applications (IDEA). It was created during the Summer of 2020 and is intended mainly for use by students participating in in-person instruction.

+
+

DISCLAIMERS

+

This is a proto-type of the application. The application is not perfect, and there can be errors in the data, as well as errors in interpretation. The application's intentions are to help users make better decisions, not provide the \"optimal\" location for the student. + Some data has been editted/removed due to possible privacy concerns.

+
") + ) + + ), + + #FIND A PLACE TO STUDY TAB + tabItem(tabName="find", + uiOutput("find_body"), + tags$div(HTML("

")), + tags$div(HTML("

Find a Place to Study

")), + fluidRow(column(12, box(collapsible = TRUE, width = '100%', title= "How to Use Find a Place to Study", style = 'color:#990000; background-color: white', solidHeader=TRUE, + HTML("
  • Open and close the dashboard menu by clicking the hamburger icon
  • +
  • Select a building by using the drop-down selection or typing to search.
  • +
  • Then click “NOW!” or select a date and time to see the building’s user activity throughout the day
  • +
  • If you wish to see a different building or hour, change your selections and click “Update Graphs”
  • ")))), + + wellPanel(actionButton(inputId = "NOW_find", label = "NOW!", width = '100%', style = ' color: white; background-color:#990000', icon = icon("calendar"), block = TRUE), + inputPanel(dateInput('date', label = 'Choose a date', value = max_date, min = min_date, max = max_date), + selectInput('time', label = 'Choose a time', time), + pickerInput(inputId = 'building', label = 'Choose a Building', + options = list(`live-search` = TRUE), + choices = list( + "Academic" = as.vector(unique((rpi_wap_last7 %>% filter(BuildingType=='academic') %>% select(Building))$Building)), + "Other On Campus" = as.vector(unique((rpi_wap_last7 %>% filter(BuildingType=='otherOnCampus') %>% select(Building))$Building)), + "Other Off Campus" = as.vector(unique((rpi_wap_last7 %>% filter(BuildingType=='otherOffCampus') %>% select(Building))$Building)), + "Greek" = as.vector(unique((rpi_wap_last7 %>% filter(BuildingType=='greek') %>% select(Building))$Building)), + "Housing" = as.vector(unique((rpi_wap_last7 %>% filter(BuildingType=='housing') %>% select(Building))$Building)))), + actionButton(inputId = "submit_find", label = "Update Graphs", icon = icon('refresh'), class = "btn-default btn-lg", style = ' color: #990000; background-color: white' ,block = TRUE) + ), + + fluidRow(column(plotOutput("OneBuildingPerHour"), width=12) ), + tags$div(HTML("

    ")), + fluidRow( + column(tags$div( + h3(tags$b(textOutput('title_summary'))), + tags$ul( + tags$li(h3(textOutput('AboveBelowAvg1_summary'))), + tags$li(h3(textOutput('AboveBelowAvg2_summary'))), + tags$li(h3(textOutput('capacity_summary'))), + tags$li(h3(textOutput('trend_summary'))), + tags$li(h3(textOutput("SmileFrown_summary"))))), width=8, offset = 2), + column(imageOutput('SmileFrown_image'),width=4, offset=4) + + ) + ) + ), + + #CAMPUS AT A GLANCE TAB + tabItem(tabName="overview", + uiOutput("overview_body"), + tags$div(HTML("

    ")), + tags$div( + HTML("

    Campus at a Glance

    ") + ), + fluidRow(column(12, box(collapsible = TRUE, width = '100%', title = 'How to Use Campus at a Glance', style = 'color:#990000; background-color: white', solidHeader=TRUE, + HTML('
  • Open and close the dashboard menu by clicking the hamburger icon
  • +
  • Select several buildings using the “Building Multiselect” drop-down selection or type to search
  • +
  • Then click “NOW!” or select a date and time to compare the user activity in each building
  • +
  • If you wish to see different buildings or hour, change your selections and click "Update Graphs"
  • ')))), + + wellPanel(actionButton(inputId = "NOW_overview", label = "NOW!", width = '100%', style = ' color: white; background-color:#990000', icon = icon("calendar"), block = TRUE), + inputPanel(dateInput('overview_date', label = 'Choose a date', value = max_date, min = min_date, max = max_date), + selectInput('overview_time', label = 'Choose a time', time), + pickerInput(inputId = "buildingGraph", label = "Building Multiselect", + choices = list( + "Academic" = as.vector(unique((rpi_wap_last7 %>% filter(BuildingType=='academic') %>% select(Building))$Building)), + "Other On Campus" = as.vector(unique((rpi_wap_last7 %>% filter(BuildingType=='otherOnCampus') %>% select(Building))$Building)), + "Other Off Campus" = as.vector(unique((rpi_wap_last7 %>% filter(BuildingType=='otherOffCampus') %>% select(Building))$Building)), + "Greek" = as.vector(unique((rpi_wap_last7 %>% filter(BuildingType=='greek') %>% select(Building))$Building)), + "Housing" = as.vector(unique((rpi_wap_last7 %>% filter(BuildingType=='housing') %>% select(Building))$Building))), + options = list(`live-search` = TRUE), + multiple = TRUE), + actionButton(inputId = "submit_campus", label = "Update Graphs", icon = icon('refresh'), class = "btn-default btn-lg", style = ' color: #990000; background-color: white' ,block = TRUE) + + ), + fluidRow(column(plotOutput('multipleBuildinghistogram'),width= 12)), + tags$div(HTML("

    ")), + fluidRow(column(plotOutput('multipleBuildingPerHour'),width= 12)) + + + ) + ), + + #CAMPUS MAP TAB + tabItem(tabName="map", + tags$div(HTML("

    ")), + tags$div( + HTML("

    Campus Map

    ") + ), + fluidRow(column(12, box(collapsible = TRUE, width = '100%', title = 'How to Use Map', style = 'color:#990000; background-color: white', solidHeader=TRUE, + HTML('
  • Open and close the dashboard menu by clicking the hamburger icon
  • +
  • Click “NOW!” or select a date and time to see the RPI campus map
  • +
  • For more information on that building, click on the marker to visit the Find a Place to Study tab
  • +
  • If you wish to see a different hour, change your selections and click “Update Graphs”
  • ')))), + + wellPanel(actionButton(inputId = "NOW_map", label = "NOW!", width = '100%', style = ' color: white; background-color:#990000', icon = icon("calendar"), block = TRUE), + inputPanel(selectInput('displaySelect', label = 'Choose a Display', c('Map', 'Table')), + dateInput('map_date', label = 'Choose a date', value = max_date, min = min_date, max = max_date), + selectInput('map_time', label = 'Choose a time', time), + pickerInput('map_building', label = 'Choose a Building', + choices = list( + "Nothing Selected" = as.vector('None'), + "Academic" = as.vector(unique(rpi_wap_last7 %>% filter(BuildingType=='academic') %>% select(Building))$Building), + "Other On Campus" = as.vector(unique(rpi_wap_last7 %>% filter(BuildingType=='otherOnCampus') %>% select(Building))$Building), + "Other Off Campus" = as.vector(unique(rpi_wap_last7 %>% filter(BuildingType=='otherOffCampus') %>% select(Building))$Building), + "Greek" = as.vector(unique(rpi_wap_last7 %>% filter(BuildingType=='greek') %>% select(Building))$Building), + "Housing" = as.vector(unique(rpi_wap_last7 %>% filter(BuildingType=='housing') %>% select(Building))$Building)), + selected = "None", multiple = FALSE, options = list(`live-search`=TRUE)), + actionButton(inputId = "submit_map", label = "Update Graphs", icon = icon('refresh'), class = "btn-default btn-lg", style = ' color: #990000; background-color: white' ,block = TRUE) + ), + uiOutput("map_body"), + tags$div(HTML("

    ")), + fluidRow(column(h4(tags$b(textOutput('MT_title_summary'))),width =12)), + tags$div(HTML("

    ")), + fluidRow(column(leafletOutput(outputId='mymap', width = "100%", height = 1000),width = 12)), + reactableOutput(outputId = 'mytable', width = '95%') + + ) + ) + ), + + #USING THE CSS FILE ARIELLE DEVELOPED + tags$script(HTML(' + $(document).ready(function() { + $(\'head\').append(\'\'); + $("header").find("nav").append(\'

    RPIStudySafe

    \'); + + }) + ')), + + + ) +) + + +# Define server logic required +server <- function(input, output, session) { + + #ABOUT PAGE BODY + output$about_body <- renderUI({}) + + + #FIND A STUDY SPOT BODY + #Now button to current time + observeEvent(input$NOW_find, { + updateDateInput(session, "date", value = max(rpi_wap_last7$Date)) + updateSelectInput(session, "time", choices = time, selected = max(rpi_wap_last7[rpi_wap_last7$Date == max(rpi_wap_last7$Date),]$Hour)) + }) + + # If user chooses minimum date we have, restrict time selection range + observe({ + if (input$date == min_date){ + updateSelectInput(session, "time", choices = time[min_time_of_min_date:length(time)]) + } + }) + + # If user chooses maximum date we have, restrict time selection range + observe({ + if (input$date == max_date){ + updateSelectInput(session, "time", choices = time[1:max_time_of_max_date]) + } + }) + + #The remaining body + output$find_body <- renderUI({ + + # When first open it, nothing to show + if (input$submit_find == 0 & is.null(input$mymap_marker_click)) + return(NULL) + + # Isolate updating inputs so that the graph will update only if user clicks "Update Graphs" + isolate({ + date_select = input$date + time_select = input$time + building_select = input$building + }) + + # Observe whether user was jumping to "Find a Place to Study", if so, update graph automatically without clicking "Update Graphs" + observe({ + # If user was jumping here, then input date, time should be same as map's, and marker should be clicked + if(input$date == input$map_date & input$time == input$map_time & !is.null(input$mymap_marker_click)) + { + date_select = input$date + time_select = input$time + building_select = input$building + } + }) + + + #QUICK SEARCH WELL PANEL + #Piping the data to only have selected building at the selected date and time + quick1_filtered <- rpi_wap_last7 %>% filter(Date == date_select) %>% filter(Building == building_select) + + #Calculations for above or below average compared to the rest of the day for the selected building + aggdf <- aggregate(quick1_filtered$users, by=list(quick1_filtered$Hour),FUN=sum) + colnames(aggdf) <- c('Hour','users') + + aggdf$users[aggdf$users <= 5] <- 0 #making users per building under 5 exluding zero -> 0 + aggdf$users[aggdf$users > 5 & aggdf$users <10 ] <- 10 #making users per building over 5 and under 10 -> 10 + + aggdf$users_z <- round((aggdf$users - mean(aggdf$users))/sd(aggdf$users), 2) # compute normalized hits per wap + aggdf$users_type <- ifelse(aggdf$users_z < 0, "below", "above") # above / below avg flag + + #calculations for above/below average compared to the rest of the buildings at that time + quick2_filtered <- rpi_wap_last7 %>% filter(Date == date_select) %>% filter(Hour == time_select) + aggdf2 <- aggregate(quick2_filtered$users, by=list(quick2_filtered$Building),FUN=sum) + colnames(aggdf2) <- c('Building','users') + + aggdf2$users[aggdf2$users <= 5] <- 0 #making users per building under 5 exluding zero -> 0 + aggdf2$users[aggdf2$users > 5 & aggdf2$users <10 ] <- 10 #making users per building over 5 and under 10 -> 10 + + aggdf2$users_z <- round((aggdf2$users - mean(aggdf2$users))/sd(aggdf2$users), 2) # compute normalized hits per wap + aggdf2$users_type <- ifelse(aggdf2$users_z < 0, "below", "above") # above / below avg flag + + #above/below average summary output + output$title_summary <- renderText({stringr::str_c('On', date_select, 'at', time.data[time.data$Time_num==time_select,]$Time_AMPM,',', building_select, ':', sep = " ")}) + output$AboveBelowAvg1_summary <- renderText({ + if(is.na(sd(aggdf$users))==TRUE){'Has no information about averages compared to earlier in the day at this time.'}else if(building_select %in% hits_per_wap_semester_by_building_max$Building & sd(aggdf$users) != 0 & (time_select %in% aggdf$Hour)){ + stringr::str_c('Has', aggdf[aggdf$Hour == time_select,]$users_type, 'average users compared to earlier in the day', sep = " ") + }else if(building_select %in% hits_per_wap_semester_by_building_max$Building & sd(aggdf$users) == 0){'Has no change in users at all today.'}else{'Has no information about averages compared to earlier in the day at this time.'} + }) + output$AboveBelowAvg2_summary <- renderText({ + if(is.na(sd(aggdf2$users))==TRUE){'Has no information about averages compared to the rest of campus at this time.'}else if(building_select %in% hits_per_wap_semester_by_building_max$Building & sd(aggdf2$users) != 0 & (time_select %in% aggdf$Hour)){ + stringr::str_c('Has', aggdf2[aggdf2$Building==building_select,]$users_type, 'average users compared to the rest of campus.', sep = " ") + }else if(building_select %in% hits_per_wap_semester_by_building_max$Building & sd(aggdf2$users) == 0){'Is no different from the rest of campus.'}else{'Has no information about averages compared to the rest of campus at this time.'} + }) + + if((time_select %in% aggdf$Hour) & (time_select != 0) & ((as.integer(time_select)-1) %in% aggdf$Hour)){ + #Calculating trend by slope over 1 hour for that building at selected hour + trend1 <- aggdf[aggdf$Hour== as.integer(time_select),]$users + trend2 <- aggdf[aggdf$Hour== as.integer(time_select)-1,]$users + trend <- as.integer(trend1)-as.integer(trend2) + + #trend summary output + output$trend_summary <- renderText({ + if(trend>=10){stringr::str_c("Has increased by",abs(trend),"users in the past hour.", sep = " ")}else if(trend<=-10){ + stringr::str_c("Has decreased by",abs(trend),"users in the past hour.", sep = " ")}else{ + stringr::str_c("Has no significant change in the amount of users in the past hour.", sep = " ") + } + + }) + }else{output$trend_summary <- renderText("Has no information about trends at this time.")} + + #calculations for OneBuildingPerHour plot + quick_plot <- rpi_wap_last7 %>% filter(Building == building_select ) %>% filter(Date == date_select) + aggdf3 <- aggregate(quick_plot$users, by=list(quick_plot$Hour, quick_plot$Building),FUN=sum) + colnames(aggdf3) <- c('Hour','Building','users') + aggdf3 <- aggdf3 %>% select(-Building) + + aggdf3$users[aggdf3$users <= 5] <- 0 #making users per building under 5 exluding zero -> 0 + aggdf3$users[aggdf3$users > 5 & aggdf3$users <10 ] <- 10 #making users per building over 5 and under 10 -> 10 + + #predictions for OneBuildingPerHour plot + if(time_select != 23 & date_select == max_date){ + Hour <- c(max(aggdf3$Hour):23) + users <- rep(c(0), times = length(Hour)) + prediction.template <- data.frame(Hour,users, stringsAsFactors=FALSE) + prediction.template[prediction.template$Hour == max(aggdf3$Hour),]$users <- aggdf3[aggdf3$Hour == max(aggdf3$Hour),]$users + + dev_select <- unique(rpi_wap_raw %>% filter(Building == building_select) %>% select(devname) ) + + weekly3_stats <- weekly3_stats %>% filter(devname %in% dev_select$devname) %>% select(-devname) %>% group_by(Day, Hour) %>% summarise_all(funs(sum)) %>% ungroup() + + input_weekday <- weekpair[day==weekdays(as.Date(date_select)),]$dayVal + + avg.df <- weekly3_stats %>% filter(Day==input_weekday) %>% select(-Day) + colnames(avg.df) <- c('Hour', 'users') + + for(i in c(max(aggdf3$Hour):22)){ + prediction.template[prediction.template$Hour == (i+1),]$users <- round((prediction.template[prediction.template$Hour==(i),]$users+ avg.df[avg.df$Hour==(i+1),]$users)/2 ) + } + + }else{ + prediction.template <- data.frame(c(0:23), rep(c(0), times = length(c(0:23))), stringsAsFactors=FALSE) + colnames(prediction.template) <- c('Hour', 'users') + } + + + #shaded rectangles to improve legibility + rect_left <- c(0, 6, 12, 18) + rectangles <- data.frame( + xmin = rect_left, + xmax = rect_left + 3, + ymin = 0, + ymax = Inf + ) + #Setting Colors + lightcolor <- "#ff2500" + darkcolor <- "#9a1600" + + + #OneBuildingPerHour plot output + output$OneBuildingPerHour <- renderPlot ({ + ggplot(aggdf3, aes(x=Hour, y=users))+ + + #Grey Time Blocks + geom_rect(data=rectangles, inherit.aes = F, aes(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax), fill='gray80', alpha=0.5) + + #Line at 0 to help legibility + geom_segment(aes(x= 0 ,xend=23,y=0,yend=0),colour=lightcolor, size=2) + + + geom_histogram(stat="identity", fill = lightcolor, binwidth = .5) + + labs(title="Investigating Where to go Today", subtitle=stringr::str_c('Checking out ', building_select, " on ", date_select, " at ", time.data[time.data$Time_num==time_select,]$Time_AMPM ), y="Users", x = 'Time of Day',color=NULL) + + scale_y_continuous(expand = expansion(mult = c(0, .1))) + + scale_x_continuous(breaks= seq(0,23,1), labels=time.data$Time_noLabel) + + + #Dark Blue Rectangle indicating selected Hour + geom_rect(fill = darkcolor, xmin = as.integer(time_select)-0.45, xmax = as.integer(time_select)+0.45, ymin = 0, ymax = aggdf3$users[as.integer(time_select)+1]) + + + #Line indicating Max users + geom_hline(yintercept = hits_per_wap_semester_by_building_max[hits_per_wap_semester_by_building_max$Building==building_select,]$capacity, linetype = "dotted") + + annotate(geom="text", label= 'Max Users this Semester', x=3, y=hits_per_wap_semester_by_building_max[hits_per_wap_semester_by_building_max$Building==building_select,]$capacity, colour = "gray30",vjust=-0.5) + + geom_vline(xintercept = as.integer(time_select), colour = "gray30") + + theme_bw() + + + #Adding bars of predicted data + geom_histogram(data = prediction.template[prediction.template$Hour>max(aggdf3$Hour),] , stat="identity", fill ="#FAFAFA", color = lightcolor, binwidth = .5) + }) + + #is the building near capacity for the selected time calculations + capacities <- hits_per_wap_semester_by_building_max %>% filter(Building==building_select) + cap_recommend <- aggdf + cap_recommend$users_action <- ifelse(cap_recommend$users <= .75*capacities$capacity, "below 75% maximum ", "near or above 75% maximum ") # above / below avg flag + cap_recommend$users_calc <- ifelse(cap_recommend$users <= .5*capacities$capacity, "below 50%", "above 50%") # above / below avg flag + + #near capacity test output statement + output$capacity_summary <- renderText({ + if(building_select %in% hits_per_wap_semester_by_building_max$Building & (time_select %in% aggdf$Hour) ){ + stringr::str_c('Is ', cap_recommend[cap_recommend$Hour==time_select,]$users_action, '(about ', round(100*(cap_recommend[cap_recommend$Hour==time_select,]$users)/ capacities[capacities$Building==building_select,]$capacity,2) ,'% full).',sep = "") + }else{'Has no information about capacity for this building at this time'} + }) + + #reccommendation calculations + recnum <- 0 + if(building_select %in% hits_per_wap_semester_by_building_max$Building){ + + if(cap_recommend[cap_recommend$Hour==time_select,]$users_action=='below 75% maximum ' & (time_select %in% aggdf$Hour)){recnum <- recnum + 1}else if(cap_recommend[cap_recommend$Hour==time_select,]$users_action=="near or above 75% maximum " & (time_select %in% aggdf$Hour)){recnum <- recnum - 1}else{} + if(cap_recommend[cap_recommend$Hour==time_select,]$users_calc=='below 50%' & (time_select %in% aggdf$Hour)){}else if(cap_recommend[cap_recommend$Hour==time_select,]$users_calc=="above 50%" & (time_select %in% aggdf$Hour)){recnum <- recnum - 0.5}else{} + if(is.na(sd(aggdf$users))==TRUE){}else if(sd(aggdf$users)==0){recnum <- recnum}else if(aggdf[aggdf$Hour==time_select,]$users_type=='above' & (time_select %in% aggdf$Hour)){recnum <- recnum - 0.75}else if(aggdf[aggdf$Hour==time_select,]$users_type=='below' & (time_select %in% aggdf$Hour)){recnum <- recnum + 0.75}else{} + if(is.na(sd(aggdf2$users))==TRUE){}else if(sd(aggdf2$users)==0){recnum <- recnum}else if(aggdf2[aggdf2$Building==building_select,]$users_type=='above' & (time_select %in% aggdf$Hour)){recnum <- recnum - 0.25}else if(aggdf2[aggdf2$Building==building_select,]$users_type=='below' & (time_select %in% aggdf$Hour)){recnum <- recnum + 0.25}else{} + + }else{} + + if((time_select %in% aggdf$Hour) & ((as.integer(time_select)-1) %in% aggdf$Hour) & (time_select != 0)){ + if(trend>=10){renum<-recnum - 0.5}else{recnum <- recnum + 0.5} + }else{} + + #reccommendation text output + output$SmileFrown_summary <- renderText({ + if((time_select %in% aggdf$Hour) & recnum>1){ + "This might be a good spot!" + }else if((time_select %in% aggdf$Hour) & recnum<0){"This might not be a good spot right now."} + else{'This may or may not be a good spot, maybe consider somewhere else first!'} + }) + + #image output depending on reccommendation calculations + output$SmileFrown_image <- renderImage({ + if((time_select %in% aggdf$Hour) & recnum>1){ + filename <- normalizePath(file.path('images', paste('WithoutBlue', '.png', sep=''))) + }else if((time_select %in% aggdf$Hour) & recnum<0){ filename <- normalizePath(file.path('images', paste('WithoutRed2', '.png', sep='')))} + else{filename <- normalizePath(file.path('images', paste('WithoutOrange2', '.png', sep='')))} + + list(src = filename, + alt = "This is alternate text for the image (smile or frown) representing the reccommendation sentence") + },deleteFile = FALSE) + + + }) + + + + #CAMPUS AT GLANCE BODY + #Now button to current time + observeEvent(input$NOW_overview, { + updateDateInput(session, "overview_date", value = max(rpi_wap_last7$Date)) + updateSelectInput(session, "overview_time", choices = time, selected = max(rpi_wap_last7[rpi_wap_last7$Date == max(rpi_wap_last7$Date),]$Hour)) + }) + + # If user chooses minimum date we have, restrict time selection range + observe({ + if (input$overview_date == min_date){ + updateSelectInput(session, "overview_time", choices = time[min_time_of_min_date:length(time)]) + } + }) + + # If user chooses maximum date we have, restrict time selection range + observe({ + if (input$overview_date == max_date){ + updateSelectInput(session, "overview_time", choices = time[1:max_time_of_max_date]) + } + }) + + #The remaining body + output$overview_body <-renderUI({ + + # When first open it, nothing to show + if (input$submit_campus == 0) + return(NULL) + + # Isolate updating inputs so that the graph will update only if user clicks "Update Graphs" + isolate({ + date_select = input$overview_date + time_select = input$overview_time + buildings_select = input$buildingGraph + + }) + + #Wes Anderson "Darjeeling1" Palette + pal <- wes_palette("Darjeeling1", length(input$buildingGraph), type = "continuous") + + #calculations for multipleBuildingPerHour plot + multi_plot <- rpi_wap_last7 %>% filter(Building %in% buildings_select) %>% filter(Date == date_select) + aggdf4 <- aggregate(multi_plot$users, by=list(multi_plot$Hour, multi_plot$Building),FUN=sum) + colnames(aggdf4) <- c('Hour','Building','users') + + aggdf4$users[aggdf4$users <= 5] <- 0 #making users per building under 5 exluding zero -> 0 + aggdf4$users[aggdf4$users > 5 & aggdf4$users <10 ] <- 10 #making users per building over 5 and under 10 -> 10 + + + cap_select <- hits_per_wap_semester_by_building_max %>% filter(Building %in% buildings_select) + aggdf4$percentage <- 0 + for(building in cap_select$Building){ + for(i in 0:23){ + aggdf4[aggdf4$Hour== i & aggdf4$Building==building, ]$percentage <- 100*(aggdf4[aggdf4$Hour== i & aggdf4$Building==building, ]$users/cap_select[cap_select$Building==building,]$capacity) + } + } + + #shaded rectangles to improve legibility + rect_left <- c(0, 6, 12, 18) + rectangles <- data.frame( + xmin = rect_left, + xmax = rect_left + 3, + ymin = 0, + ymax = Inf + ) + #Setting Colors + lightcolor <- "#ff2500" + darkcolor <- "#9a1600" + + #Multiple Building per Hour Plot + output$multipleBuildingPerHour <- renderPlot({ + ggplot(aggdf4, aes(x=Hour)) + + #Grey Time Blocks + geom_rect(data=rectangles, inherit.aes = F, + aes(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax), + fill='gray80', alpha=0.2) + + geom_line(aes(y=percentage, col=Building)) + scale_color_brewer(palette = "RdYlBu")+ + labs(title= "Investigating Where to Go Today", + subtitle=paste("Checking out the Selected Buildings on", date_select, "at", time.data[time.data$Time_num==time_select,]$Time_AMPM ), + y="Percentage of Maximum Users", x = "Time of Day", + color=NULL) + scale_x_continuous(breaks= seq(0,23,1), labels=time.data$Time_noLabel) + scale_y_continuous(breaks= seq(0,100,5)) + + geom_vline(xintercept = as.integer(time_select)) + + scale_color_manual(values = pal) + + theme_bw() + + theme(legend.position = "bottom") + }) + + + # Select the specific hour (user input) data out + Builings_specific_hour2 <- aggdf4[aggdf4$Hour == time_select,] + + # The data frame of maximum urses of every building for this semester + Buildings_selected_capacity <- hits_per_wap_semester_by_building_max[hits_per_wap_semester_by_building_max$Building %in% Builings_specific_hour2$Building,] + + # Combine maximum urses(capacity here) of each building and the data we have + Builings_specific_hour <- cbind(Builings_specific_hour2, capacity = rep(0,nrow(Builings_specific_hour2))) + Builings_specific_hour$capacity[Builings_specific_hour$Building %in% Buildings_selected_capacity$Building] <- Buildings_selected_capacity$capacity + + # Split into two data frames in order to draw two histograms(one is capacity, the other is number of users now) in one graph + true_users <- data.frame(Building = Builings_specific_hour$Building, users = Builings_specific_hour$users) + max_capacity <- data.frame(Building = Builings_specific_hour$Building, users = Builings_specific_hour$capacity) + + # Plot the histogram graph + output$multipleBuildinghistogram <- renderPlot({ + + ggplot(Builings_specific_hour,aes(x = Building, y = users, color = as.factor(Building), fill = as.factor(Building))) + + geom_histogram(data = true_users, stat='identity', alpha=0.6) + geom_histogram(data = max_capacity, fill = 'white', stat='identity', alpha=0.6, label = TRUE) + + geom_text(aes(label = percent(users/capacity)), position = position_stack(vjust = 1), size=5) + + labs(title="Investigating Where to Go Today", + subtitle=paste("Checking out the Selected Buildings on", date_select, "at", time.data[time.data$Time_num==time_select,]$Time_AMPM ), + y="Users Compared to Maximum", x = "Selected Building(s)", + color = 'Buildings', + fill = 'Buildings' + ) + + scale_color_manual(values = pal) + scale_fill_manual(values = pal) + + theme_bw() + + theme(legend.position = "bottom") + }) + + }) + + + #MAP TAB BODY + date_map <- reactiveValues(date = 'none') # Remember the input date for future use + time_map <- reactiveValues(time = 0) # Remember the input time for future use + counter_map <- reactiveValues(number = 0) # Number of "Update Graphs" button being clicked + + #Now button to current time + observeEvent(input$NOW_map, { + date_map$date <- max_date + time_map$time <- max_time_of_max_date-1 + updateDateInput(session, "map_date", value = max_date) + updateSelectInput(session, "map_time", choices = time[1:max_time_of_max_date], selected = max_time_of_max_date-1) + }) + + # If user chooses minimum date we have, restrict time selection range + observe({ + if (input$map_date == min_date){ + updateSelectInput(session, "map_time", choices = time[min_time_of_min_date:length(time)]) + } + }) + + # If user chooses maximum date we have, restrict time selection range + observe({ + if (input$map_date == max_date){ + updateSelectInput(session, "map_time", choices = time[1:max_time_of_max_date]) + } + }) + + # If user chooses date between maximum and minimum, then time should not be constrained + observe({ + if (input$map_date != max_date & input$map_date != min_date){ + updateSelectInput(session, "map_time", choices = time) + } + }) + + #The remaining body + jump <- 'no' # Jump to "Find a Place to Study" when this jump = 'yes' + building_map <- reactiveValues(building = 'none') # Temporarily remember the name of building that is clicked + + output$map_body <- renderUI({ + + # When first open it, nothing to show + if (input$submit_map == 0 & input$NOW_map == 0) + return(NULL) + + # Observe whether "Update Graphs" button is clicked + observe({ + if (input$submit_map == counter_map$number + 1){ + date_map$date <- input$map_date + time_map$time <- input$map_time + counter_map$number <- counter_map$number + 1 + } + }) + + date_select <- date_map$date + time_select <- time_map$time + MTpreference <- input$displaySelect + + #Title output + output$MT_title_summary <- renderText({ + stringr::str_c("RPI on ", date_select,' at ', time.data[time.data$Time_num==time_select,]$Time_AMPM) + }) + + #Creating a data set for the map and table + map_merge_filtered <- rpi_wap_last7 %>% filter(Date == date_select) %>% filter(Hour==time_select) %>% + select(lat, lng, BuildingType,Building, users) %>% group_by(lat, lng, BuildingType,Building) %>% dplyr::summarise(totalusers = sum(users)) + + map_merge_filtered$totalusers[map_merge_filtered$totalusers <= 5] <- 0 #making users per building under 5 exluding zero -> 0 + map_merge_filtered$totalusers[map_merge_filtered$totalusers > 5 & map_merge_filtered$totalusers <10 ] <- 10 #making users per building over 5 and under 10 -> 10 + + # Combine our data with capacity + map_merge_filtered2 <- merge(map_merge_filtered, hits_per_wap_semester_by_building_max, by.x= "Building", by.y="Building") + map_merge_filteredT <- map_merge_filtered2 + + # If user select a building, then remember info of that building as building_selected and delete it from map_merge_filtered2 + if (input$map_building != 'None'){ + building_selected <- map_merge_filtered2[map_merge_filtered2$Building == input$map_building,] + map_merge_filtered2 <- map_merge_filtered2[-c(which(map_merge_filtered2$Building == input$map_building)),] + } + + #possible colors for map + #pal2 <- c("#7fa9ae","#1f6798","#00205b", "#990000") + + # Get color of marker according to percentage of "capacity" + getColor <- function(map_merge_filtered2) { + mapply(function(totalusers, capacity) { + if(totalusers <= 0.25*capacity) { + "lightblue" + #"#1f6798" #light blue + } else if(totalusers <= 0.5*capacity) { + "beige" + #"#00205b" #dark blue + } else if(totalusers <= 0.75*capacity) { + "lightred" + #"#333366" #purple + }else { + "red" + #"#990000" #red + } }, map_merge_filtered2$totalusers, map_merge_filtered2$capacity + ) + } + + icons <- awesomeIcons( + icon = 'ios-close', + iconColor = 'black', + library = 'ion', + markerColor = getColor(map_merge_filtered2) + ) + + #Hiding whichever display is not selected, map or table + observeEvent(MTpreference, { + if(MTpreference=='Map'){ + hideElement(id = 'mytable', anim = FALSE) + showElement(id = 'mymap') + }else if(MTpreference == 'Table'){ + hideElement(id = 'mymap', anim = FALSE) + showElement(id = 'mytable') + } + }) + + #creating table + #Add percentage column + map_merge_filteredT$percentage <- percent(map_merge_filteredT$totalusers/map_merge_filteredT$capacity) + + output$mytable <- renderReactable({ + reactable( + map_merge_filteredT, + showPageSizeOptions = TRUE, + pageSizeOptions = c(5, 10, 25, 50, 100), + highlight = TRUE, + outlined = TRUE, + bordered = TRUE, + striped = TRUE, + compact = TRUE, + wrap = TRUE, + showSortable = TRUE, + resizable = TRUE, + searchable = TRUE, + paginationType = "jump", + style = 'color: black; background-color:#FAFAFA;', + defaultSortOrder = "desc", + columns = list( + lat = colDef(show = FALSE), + lng = colDef(show = FALSE), + Building = colDef(filterable = TRUE, style = 'text-align: left;'), #, onclick = 'Shiny.setInputValue(\"link1\", this.id, {priority: \"event\"})'), + BuildingType = colDef(name = "Building Type", filterable = TRUE, style = 'text-align: left;'), + totalusers = colDef(name = "Total Number of Users", align = 'left', style = 'text-align: right;'), + capacity = colDef(name = "Max Number of Users", align = 'left', style = 'text-align: right;'), + percentage = colDef(name = "Percentage of Max", format = colFormat(percent = TRUE), align = 'left', style = 'text-align:right;' ) + ), + columnGroups = list( + colGroup(name = '', columns = c("Building", "BuildingType", "totalusers", "capacity", "percentage"), headerStyle = 'color: white; background-color:black;')) + + ) + + + }) + + + #displaying the resulting map + + map <- leaflet(map_merge_filtered2) %>% + addAwesomeMarkers(icon=icons,popup = ~paste0("Building: ", map_merge_filtered2$Building, "
    ", "Total number of Users: ", map_merge_filtered2$totalusers, "
    ", + "Percentage of Capacity: ", percent(map_merge_filtered2$totalusers/map_merge_filtered2$capacity),"
    ", "Learn more about ", "", map_merge_filtered2$Building, "", + " click ", actionLink(inputId = "jump_to_find", label = "here", onclick = 'Shiny.setInputValue(\"link1\", this.id, {priority: \"event\"})'))) %>% + addTiles() %>%setView( lng = -73.6789, lat = 42.7298, zoom = 16 ) %>% + #%>% addControl(map_title, position = "topright", className="map-title") %>% + addLegend(position = "topleft", colors = c("#00BFFF", "orange", "#FF6347", "red"), labels = c("0% ~ 25%", "25% ~ 50%", "50% ~ 75%", "75% ~ 100%")) + + # If user selects a building, then add star icon at that building's position and star legend + if (input$map_building != 'None') { + + # Using different color of star as icon and legend according to percentage of capacity + if (building_selected$totalusers <= 0.25*building_selected$capacity){ + icon_url <- "images/blue_star.png" + html_legend <- " Place to go
    " + } + else if (building_selected$totalusers <= 0.5*building_selected$capacity){ + icon_url <- "images/beige_star.png" + html_legend <- " Place to go
    " + } + else if (building_selected$totalusers <= 0.75*building_selected$capacity){ + icon_url <- "images/lightred_star.png" + html_legend <- " Place to go
    " + } + else { + icon_url <- "images/darkred_star.png" + html_legend <- " Place to go
    " + } + + # Make star icon + star_icon <- makeIcon( + iconUrl = icon_url, + iconWidth = 40, iconHeight= 40, iconAnchorX= 33*215/230/2, iconAnchorY = 16 + ) + + # Add icon and legend + map <- map %>% addMarkers(icon=star_icon,lng = building_selected$lng, lat = building_selected$lat,popup = ~paste0("Building: ", building_selected$Building, "
    ", "Total number of Users: ", building_selected$totalusers, "
    ", + "Percentage of Capacity: ", percent(building_selected$totalusers/building_selected$capacity),"
    ", "Learn more about ", "", building_selected$Building, "", + " click ", actionLink(inputId = "jump_to_find", label = "here", onclick = 'Shiny.setInputValue(\"link1\", this.id, {priority: \"event\"})'))) %>% + addControl(html = html_legend, position = "topleft") + } + + output$mymap <- renderLeaflet({map}) + + + + + + + # If the action link is not clicked, return nothing + if (is.null(input$link1)) + return() + # Isolate everything so that they just excute when user clicks the link + isolate({ + + # To determine name of building that is clicked + marker_info <- input$mymap_marker_click + # marker_info just has latitude and longitude so we need to pair them with our data to find the name of building + marker_building <- map_merge_filtered[map_merge_filtered$lat == marker_info$lat,] + marker_building <- marker_building[marker_building$lng == marker_info$lng,] + + # To make sure that if user go back to map after jumping to "Find a Place to Study", system will not jump back to "Find a Place to Study" + if(building_map$building != marker_building$Building) + { + jump <- 'yes' + building_map$building <- marker_building$Building + } + }) + + # If jump = yes, jump to "Find a Place to Study" and update everything + if (jump == 'yes') { + + # Switch to "Find a Place to Study" + newtab <- switch(input$tabs, "map" = "find","find" = "map") + updateTabItems(session, "tabs", newtab) + + + # Update date, time, and building selected of "Find a Place to Study" + updateDateInput(session, "date", value = date_select) + updateSelectInput(session, "time", choices = time, selected = time_select) + updateSelectInput(session, "building", + choices = list( + "Academic" = as.vector(unique(rpi_wap_last7 %>% filter(BuildingType=='Academic') %>% select(Building))$Building), + "Other On Campus" = as.vector(unique(rpi_wap_last7 %>% filter(BuildingType=='OtherOnCampus') %>% select(Building))$Building), + "Other Off Campus" = as.vector(unique(rpi_wap_last7 %>% filter(BuildingType=='OtherOffCampus') %>% select(Building))$Building), + "Greek" = as.vector(unique(rpi_wap_last7 %>% filter(BuildingType=='Greek') %>% select(Building))$Building), + "Housing" = as.vector(unique(rpi_wap_last7 %>% filter(BuildingType=='Housing') %>% select(Building))$Building)), + selected = marker_building$Building) + + jump <- 'no' + } + + }) + + +} + +# Run the application +shinyApp(ui = ui, server = server) \ No newline at end of file diff --git a/images/WithoutBlue.png b/images/WithoutBlue.png new file mode 100644 index 00000000..ac39d3c4 Binary files /dev/null and b/images/WithoutBlue.png differ diff --git a/images/WithoutOrange2.png b/images/WithoutOrange2.png new file mode 100644 index 00000000..068df5aa Binary files /dev/null and b/images/WithoutOrange2.png differ diff --git a/images/WithoutRed2.png b/images/WithoutRed2.png new file mode 100644 index 00000000..e9784ac9 Binary files /dev/null and b/images/WithoutRed2.png differ diff --git a/images/beige_star.png b/images/beige_star.png new file mode 100644 index 00000000..2b6b555c Binary files /dev/null and b/images/beige_star.png differ diff --git a/images/blue_star.png b/images/blue_star.png new file mode 100644 index 00000000..64d6a615 Binary files /dev/null and b/images/blue_star.png differ diff --git a/images/darkred_star.png b/images/darkred_star.png new file mode 100644 index 00000000..0e4e6e5c Binary files /dev/null and b/images/darkred_star.png differ diff --git a/images/lightred_star.png b/images/lightred_star.png new file mode 100644 index 00000000..1a0b78b8 Binary files /dev/null and b/images/lightred_star.png differ diff --git a/read_me_wap_data.R b/read_me_wap_data.R new file mode 100644 index 00000000..bbb627f8 --- /dev/null +++ b/read_me_wap_data.R @@ -0,0 +1,65 @@ +#Reading in the data and necessary libraries for RPI StudySafe App +#Created by Kara Kniss + +#Reading in the necessary libraries +library(shiny) +library(shinydashboard) +library(shinyjs) +library(ggplot2) +library(shinyWidgets) +library(tidyverse) +library(tidyr) +library(lubridate) +library(xlsx) +library(plyr) +library(scales) +library(zoo) +library(ggalt) +library(leaflet) +library(RColorBrewer) +library(viridis) +library(reactable) + +###READING IN NECESSARY FILES + +#reading in the WAP last seven days data + +rpi_wap_raw <- readRDS("../../COVID_RPI_WiFi_Data/rpi_wap_raw.rds") +combined_wap_data <- readRDS("../../COVID_RPI_WiFi_Data/combined_wap_data.rds") + +#Reading in WAP semester with devname summary statistics (usercount mean, median, max) +rpi_wap_stats <- readRDS("../../COVID_RPI_WiFi_Data/rpi_wifi_semester_day_summary.rds") + + +#Reading in semester WAP maximums per building data +# hits_per_wap_semester_by_building_max <- readRDS("wap_data/rpi_wap_semester_max.Rds") + +###CLEANING DATA FOR USAGE IN APP + +#rpi_wap_raw: getting devname, users, Date, Building, Hour +rpi_wap_raw <- rpi_wap_raw %>% mutate(Hour = hour(as.POSIXct(time))) %>% select(devname, usercount, Date, Building, Hour) +colnames(rpi_wap_raw) <- c('devname', 'users', 'Date', 'Building', 'Hour') + +#getting buildings to append to devnames +bldgs <- rpi_wap_raw %>% filter(Date == min(rpi_wap_last7$Date)+1) %>% filter(Hour==12) %>% select(devname, Building) + +# Get maximum number of users for each building +hits_per_wap_semester_by_building_max <- merge(rpi_wap_stats, bldgs, by.x= "devname", by.y="devname") +hits_per_wap_semester_by_building_max <- hits_per_wap_semester_by_building_max %>% group_by(devname, Building, Day) %>% summarise_all(funs(max)) %>% ungroup() %>% select(devname, Day, usercount_max, Building) +hits_per_wap_semester_by_building_max <- hits_per_wap_semester_by_building_max[,2:ncol(hits_per_wap_semester_by_building_max)] +hits_per_wap_semester_by_building_max <- hits_per_wap_semester_by_building_max %>% group_by(Building, Day) %>% summarise_all(funs(sum)) %>% ungroup() +hits_per_wap_semester_by_building_max <- hits_per_wap_semester_by_building_max %>% group_by(Building) %>% summarise_all(funs(max)) %>% ungroup() %>% select(Building, usercount_max) +colnames(hits_per_wap_semester_by_building_max) <- c('Building', 'capacity') + + +#combined_wap_data: Building, lat, lng +combined_wap_data <- combined_wap_data %>% group_by(Building, latitude, longitude, time, Date) %>% summarise_all(funs(max)) %>% ungroup() +combined_wap_data <- combined_wap_data %>% mutate(Hour = hour(as.POSIXct(time))) %>% filter(Date== min(combined_wap_data$Date)+1) %>% filter(Hour == 12) %>% select(Building, latitude, longitude) +colnames(combined_wap_data) <- c('Building','lat','lng' ) + +combined_wap_data$BuildingType <- '' +combined_wap_data[combined_wap_data$Building %in% Academic == TRUE, ]$BuildingType <- 'Academic' +combined_wap_data[combined_wap_data$Building %in% Greek == TRUE, ]$BuildingType <- 'Greek' +combined_wap_data[combined_wap_data$Building %in% Housing == TRUE, ]$BuildingType <- 'Housing' +combined_wap_data[combined_wap_data$Building %in% OtherOnCampus == TRUE, ]$BuildingType <- 'OtherOnCampus' +combined_wap_data[combined_wap_data$Building %in% OtherOffCampus == TRUE, ]$BuildingType <- 'OtherOffCampus' diff --git a/read_wapData.R b/read_wapData.R new file mode 100644 index 00000000..2da6251f --- /dev/null +++ b/read_wapData.R @@ -0,0 +1,106 @@ +#Reading in the data and necessary libraries and data for RPI StudySafe App +#Created by Kara Kniss + +################################################################################################################ +#READING IN THE NECESSARY LIBRARIES +################################################################################################################ +library(shiny) +library(shinydashboard) +library(shinyjs) +library(ggplot2) +library(shinyWidgets) +library(tidyverse) +library(tidyr) +library(lubridate) +library(plyr) +library(scales) +library(zoo) +library(ggalt) +library(leaflet) +library(plotly) +library(wesanderson) +library(reactable) + +################################################################################################################ +###READING IN NECESSARY FILES +################################################################################################################ +#rpi_wap_raw: Min_30, devname, maccount, usercount, datetime, date_time, Date, Building, Floor, Room, latitude, longitude, buldingType, abbrev, time +rpi_wap_raw <- readRDS("../../COVID_RPI_WiFi_Data/rpi_wap_raw.rds") + +#combined_wap_data: building, time, Date, latitude, longitude, buildingType, abbrev, users, macs, +combined_wap_data <- readRDS("../../COVID_RPI_WiFi_Data/combined_wap_data.rds") + +#rpi_wap_stats: devname, Day, maccount_mean, maccount_med, maccount_max, usrecount_mean, usercount_med, usercount_max +rpi_wap_stats <- readRDS("../../COVID_RPI_WiFi_Data/rpi_wifi_semester_day_summary.rds") + +#rpi_wap_week: devname, maccount, usercount, datetime, date_time, Date, Day +rpi_wap_week <- readRDS("../../COVID_RPI_WiFi_Data/rpi_wifi_semester_extended.rds") + +#buildinginfo: Building, latitude, longitude, buildingType, abbrev +#buildinginfo <- readRDS("../../COVID_RPI_WiFi_Data/buildinginfo.rds") + +################################################################################################################ +###CLEANING DATA +################################################################################################################ +#rpi_wap_raw: devname, users, Date, Building, Hour, lat, lng, BuildingType +rpi_wap_raw <- rpi_wap_raw %>% mutate(Hour = hour(as.POSIXct(time))) %>% select(devname, usercount, Date, Building, Hour, latitude, longitude, buildingType) +colnames(rpi_wap_raw) <- c('devname', 'users', 'Date', 'Building', 'Hour', 'lat', 'lng', 'BuildingType') + +#combined_wap_data: Building, Hour, lat, lng, BuildingType +rpi_wap_last7 <- combined_wap_data %>% mutate(Hour = hour(as.POSIXct(time))) %>% select(Building, users, Date, Building, Hour, latitude, longitude, buildingType) +rpi_wap_last7 <- rpi_wap_last7 %>% group_by(Building, Date, Hour, latitude, longitude, buildingType) %>% summarise_all(funs(max)) %>% ungroup() +colnames(rpi_wap_last7) <- c('Building', 'Date', 'Hour','lat','lng', 'BuildingType', 'users' ) + +#Building Tagging, Use Later for whatever categories we have +#combined_wap_data$BuildingType <- '' +#combined_wap_data[combined_wap_data$Building %in% Academic == TRUE, ]$BuildingType <- 'Academic' +#combined_wap_data[combined_wap_data$Building %in% Greek == TRUE, ]$BuildingType <- 'Greek' +#combined_wap_data[combined_wap_data$Building %in% Housing == TRUE, ]$BuildingType <- 'Housing' +#combined_wap_data[combined_wap_data$Building %in% OtherOnCampus == TRUE, ]$BuildingType <- 'OtherOnCampus' +#combined_wap_data[combined_wap_data$Building %in% OtherOffCampus == TRUE, ]$BuildingType <- 'OtherOffCampus' + +#bldgs(getting buildings to append to devnames): devname, Building +bldgs <- rpi_wap_raw %>% filter(Date == min(rpi_wap_last7$Date)+1) %>% filter(Hour==12) %>% select(devname, Building) + +# hits_per_wap_semester_by_building_max(getting maximum number of users for each building): Building, capacity +hits_per_wap_semester_by_building_max <- merge(rpi_wap_stats, bldgs, by.x= "devname", by.y="devname") +hits_per_wap_semester_by_building_max <- hits_per_wap_semester_by_building_max %>% group_by(devname, Building, Day) %>% summarise_all(funs(max)) %>% ungroup() %>% select(devname, Day, usercount_max, Building) +hits_per_wap_semester_by_building_max <- hits_per_wap_semester_by_building_max[,2:ncol(hits_per_wap_semester_by_building_max)] +hits_per_wap_semester_by_building_max <- hits_per_wap_semester_by_building_max %>% group_by(Building, Day) %>% summarise_all(funs(sum)) %>% ungroup() +hits_per_wap_semester_by_building_max <- hits_per_wap_semester_by_building_max %>% group_by(Building) %>% summarise_all(funs(max)) %>% ungroup() %>% select(Building, usercount_max) +colnames(hits_per_wap_semester_by_building_max) <- c('Building', 'capacity') + +################################################################################################################ +## DEFININING LISTS AND DATA FRAMES FOR CONVENIENCE +################################################################################################################ + +#Possible tagging for later +#placesOfInterest <- c('CII', 'DCC', 'Folsom Library', 'Rensselaer Union', 'Voorhees Computing Center') +#placesOfWellness <- c('ASRC', 'ECAV arena', 'ECAV stadium', 'Houston Field House', 'Fitness Center', '87 Gym', 'Robison Pool') +#placesOfDeliciousness <- c('Moes, College Ave', 'Commons Dining Hall', 'Russell Sage Dining Hall') +#BuildingTypes <- c('Academic','Housing','placesOfInterest', 'placesOfWellness', 'placesOfDeliciousness', 'Greek', 'OtherOnCampus', 'OtherOffCampus') + +#creating a time list for the graphs so they aren't in military time +time <- list('12:00am'= 0,'1:00am'=1, '2:00am'=2, '3:00am'=3, '4:00am'=4, '5:00am'=5, '6:00am'=6, '7:00am'=7, '8:00am'=8, '9:00am'=9, '10:00am'=10, "11:00am"=11, "12:00pm"=12, "1:00pm"=13, "2:00pm"=14, "3:00pm"=15, "4:00pm"=16, "5:00pm"=17, "6:00pm"=18, "7:00pm"=19, "8:00pm"=20, "9:00pm"=21,"10:00pm"=22, "11:00pm"=23) +Time_num <- c(0:23) +Time_AMPM <- c('12am', '1am', '2am', '3am', '4am', '5am', '6am', '7am', '8am', '9am', '10am', '11am', '12pm', '1pm', '2pm','3pm', '4pm', '5pm', '6pm', '7pm', '8pm', '9pm', '10pm', '11pm' ) +Time_noLabel <- c(12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11) +time.data <- data.frame(Time_num, Time_AMPM, Time_noLabel, stringsAsFactors=FALSE) + +#defining the max date and min date info +min_date <- min(rpi_wap_last7$Date) +max_date <- max(rpi_wap_last7$Date) +max_time_of_max_date <- max(rpi_wap_last7[rpi_wap_last7$Date == max(rpi_wap_last7$Date),]$Hour)+1 +min_time_of_min_date <- min(rpi_wap_last7[rpi_wap_last7$Date == min(rpi_wap_last7$Date),]$Hour)+1 + +#weekday information +#getting selected weekday +day <- c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ) +dayVal<- c(1,2,3,4,5,6,7) +weekpair <- data.frame(day,dayVal, stringsAsFactors=FALSE) + +#weekly3_stats(Getting average devname users of the last three weeks by day): devname, Day, Hour, users +weekly3_stats <- rpi_wap_week %>% filter(Date >= min(rpi_wap_last7$Date)-21) %>% mutate(Hour = hour(as.POSIXct(date_time))) +weekly3_stats <- weekly3_stats %>% select(devname, usercount, Day, Hour) %>% group_by(devname, Day, Hour) %>% summarise_all(funs(max)) %>% ungroup() +colnames(weekly3_stats) <- c('devname', 'Day', 'Hour', 'users') + diff --git a/www/Rensselaer.png b/www/Rensselaer.png new file mode 100644 index 00000000..0aa27bec Binary files /dev/null and b/www/Rensselaer.png differ diff --git a/www/Rensselaer_round.png b/www/Rensselaer_round.png new file mode 100644 index 00000000..f07f61f0 Binary files /dev/null and b/www/Rensselaer_round.png differ diff --git a/www/backtoschool_style.css b/www/backtoschool_style.css new file mode 100644 index 00000000..f3b23994 --- /dev/null +++ b/www/backtoschool_style.css @@ -0,0 +1,235 @@ + +/*--------------------BACK-TO-SCHOOL---------------------*/ + + +.footer{ + text-align: center; + margin-bottom: 5%; + margin-top: 4.5vh; + position: absolute; + width: 100%; + font-size: 1.5em; + color: #900000; + font-weight: 300; +} + + +/*-------Landing Page-------*/ + + /*flex container styling*/ + + .flex-container { + --width-mobile: 12; + display: -ms-flex; + display: -webkit-flex; + display: flex; + margin: 0 -10px; + justify-content: space-around; + flex-wrap: wrap; + height: inherit; + padding-right: 10vw; + padding-left: 10vw; + } + +.column { + --columns: 12; /* number of columns in the grid system */ + --width: var(--width-mobile, 0); /* default width of the element */ + flex-basis: calc(var(--width) / var(--columns) * 100%); +} + +@media (min-width: 576px) { + .column { + --width: var(--width-desktop, var(--width-mobile, 0)); + } +} + +.flex-div { + --width-desktop: 4; + --width-mobile: 12; + --width-tablet: 6; + padding: 1%; + align-items: center; + margin: 0 -10px; + max-width: 250px; +} + +.primary_school{ + height: 155px; + background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 226.36 164.75'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%236e6e6e;%7D.cls-2%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Cg id='Layer_2' data-name='Layer 2'%3E%3Cg id='Layer_1-2' data-name='Layer 1-2'%3E%3Cpath class='cls-1' d='M141.61,58.88H127V49.71L132.1,54l2.16-2.55L113.18,33.66,92.12,51.47,94.28,54l5.1-4.31v9.17H84.75L65.24,83.14h8.65v39.32h78.58V83.14h8.65ZM101.78,46.76l11.4-9.64,11.4,9.64v13h-22.8Zm-8.38,70.7H82.83V105.27H93.4Zm0-19H82.83V90.37a5.29,5.29,0,0,1,10.57,0Zm19.2,22.91h-8v-16.1h8Zm-4.71-22.91V90.37a5.29,5.29,0,1,1,10.58,0v8.12Zm13.9,22.91h-8v-16.1h8Zm21.74-3.94H133V105.27h10.57Zm0-19H133V90.37a5.29,5.29,0,0,1,10.57,0Z'/%3E%3Cpath class='cls-1' d='M124.58,46.76l-11.4-9.64-11.4,9.64v13h22.8Zm-.92,12.12h-21v-12L113.18,38l10.48,8.86Z'/%3E%3Cpath class='cls-1' d='M111.29,43.42a1.13,1.13,0,0,0,0,.26A5.61,5.61,0,0,0,107.66,48l-1,5.74c-.61.38-1,.81-1,1.28,0,1.44,3.34,2.61,7.45,2.61s7.44-1.17,7.44-2.61c0-.47-.34-.9-1-1.28l-1-5.74a5.62,5.62,0,0,0-3.65-4.35,1.13,1.13,0,0,0,0-.26,1.89,1.89,0,0,0-3.78,0h0Zm1.89-1a1,1,0,0,1,1,1h0a5.6,5.6,0,0,0-2,0h0a1,1,0,0,1,1-1h0Zm.14,13.34a1.25,1.25,0,0,0,1.26-1.26h0a1,1,0,0,0,0-.25c2.48.12,4.32.55,4.32,1.06s-2.52,1.09-5.63,1.09-5.64-.49-5.64-1.09,1.93-1,4.49-1.07a1.13,1.13,0,0,0,0,.26,1.26,1.26,0,0,0,1.2,1.24Z'/%3E%3Crect class='cls-2' width='226.36' height='164.75'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A"); + background-repeat: no-repeat; + background-position: center; + background-size: 80%; +} + +.primary_school:hover{ + background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 226.36 164.75'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%23900000;%7D.cls-2%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Cg id='Layer_2' data-name='Layer 2'%3E%3Cg id='Layer_1-2' data-name='Layer 1-2'%3E%3Cpath class='cls-1' d='M141.61,58.88H127V49.71L132.1,54l2.16-2.55L113.18,33.66,92.12,51.47,94.28,54l5.1-4.31v9.17H84.75L65.24,83.14h8.65v39.32h78.58V83.14h8.65ZM101.78,46.76l11.4-9.64,11.4,9.64v13h-22.8Zm-8.38,70.7H82.83V105.27H93.4Zm0-19H82.83V90.37a5.29,5.29,0,0,1,10.57,0Zm19.2,22.91h-8v-16.1h8Zm-4.71-22.91V90.37a5.29,5.29,0,1,1,10.58,0v8.12Zm13.9,22.91h-8v-16.1h8Zm21.74-3.94H133V105.27h10.57Zm0-19H133V90.37a5.29,5.29,0,0,1,10.57,0Z'/%3E%3Cpath class='cls-1' d='M124.58,46.76l-11.4-9.64-11.4,9.64v13h22.8Zm-.92,12.12h-21v-12L113.18,38l10.48,8.86Z'/%3E%3Cpath class='cls-1' d='M111.29,43.42a1.13,1.13,0,0,0,0,.26A5.61,5.61,0,0,0,107.66,48l-1,5.74c-.61.38-1,.81-1,1.28,0,1.44,3.34,2.61,7.45,2.61s7.44-1.17,7.44-2.61c0-.47-.34-.9-1-1.28l-1-5.74a5.62,5.62,0,0,0-3.65-4.35,1.13,1.13,0,0,0,0-.26,1.89,1.89,0,0,0-3.78,0h0Zm1.89-1a1,1,0,0,1,1,1h0a5.6,5.6,0,0,0-2,0h0a1,1,0,0,1,1-1h0Zm.14,13.34a1.25,1.25,0,0,0,1.26-1.26h0a1,1,0,0,0,0-.25c2.48.12,4.32.55,4.32,1.06s-2.52,1.09-5.63,1.09-5.64-.49-5.64-1.09,1.93-1,4.49-1.07a1.13,1.13,0,0,0,0,.26,1.26,1.26,0,0,0,1.2,1.24Z'/%3E%3Crect class='cls-2' width='226.36' height='164.75'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A"); + background-size: 85%; +} + +.secondary_school{ + height: 155px; + background-repeat: no-repeat; + background-position: center; + background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 226.36 164.75'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%236e6e6e;%7D.cls-2%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Cg id='Layer_2' data-name='Layer 2'%3E%3Cg id='Layer_1-2' data-name='Layer 1-2'%3E%3Cpath class='cls-1' d='M67.46,117.49h91.36V83.6H67.46Zm76.32-18.58a4.77,4.77,0,1,1,9.54,0h0V107h-9.54Zm-13.74,0a4.78,4.78,0,0,1,9.55-.44,3.23,3.23,0,0,1,0,.44V107H130Zm-24.83,3.33a7.93,7.93,0,0,1,7-7.86V93.19h2.1v1.22a7.92,7.92,0,0,1,6.79,7.83v13.43h-6.79v1.66h-2.1v-1.66h-7ZM86.7,98.87a4.77,4.77,0,1,1,9.54,0h0V107H86.7Zm-13.7,0a4.77,4.77,0,0,1,9.54,0h0V107H73Z'/%3E%3Cpath class='cls-1' d='M67.46,80.45h91.36V49.7H67.46Zm76.4-14.57a4.78,4.78,0,0,1,9.55,0V74h-9.55Zm-13.73,0a4.77,4.77,0,1,1,9.54,0h0V74h-9.54Zm-10,2.63V59a4.78,4.78,0,0,1,4.79,4.77h0V74H120.1Zm-11.65-9a4.78,4.78,0,0,1,9.56,0V74h-9.56Zm-6.88,4.14a4.78,4.78,0,0,1,4.78-4.78v15H101.6Zm-14.82,2.2a4.78,4.78,0,0,1,9.55,0v8.08H86.78ZM73,65.88a4.78,4.78,0,0,1,9.55,0V74H73Z'/%3E%3Crect class='cls-1' x='112.18' y='115.67' width='2.1' height='1.66'/%3E%3Cpath class='cls-1' d='M114.28,94.41V93.19h-2.1v1.19a8.39,8.39,0,0,1,1-.06A10,10,0,0,1,114.28,94.41Z'/%3E%3Cpath class='cls-1' d='M114.28,115.67V94.41a7.3,7.3,0,0,0-1.14-.09,8.39,8.39,0,0,0-1,.06v21.29Z'/%3E%3Cpolygon class='cls-1' points='113.97 42.44 112.66 42.44 62.49 42.44 62.49 47.33 163.79 47.33 163.79 42.44 113.97 42.44'/%3E%3Crect class='cls-1' x='112.66' y='26.06' width='1.31' height='16.38'/%3E%3Cpolygon class='cls-1' points='123.07 29.89 126.72 26.06 114.93 26.06 114.93 33.72 126.72 33.72 123.07 29.89'/%3E%3Crect class='cls-1' x='105.21' y='119.01' width='15.85' height='2.33'/%3E%3Crect class='cls-1' x='103.39' y='123.44' width='19.51' height='2.33'/%3E%3Crect class='cls-1' x='101.8' y='127.73' width='22.68' height='2.33'/%3E%3Crect class='cls-2' width='226.36' height='164.75'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + background-size: 80%; + +} + +.secondary_school:hover{ + background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 226.36 164.75'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%23900000;%7D.cls-2%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Cg id='Layer_2' data-name='Layer 2'%3E%3Cg id='Layer_1-2' data-name='Layer 1-2'%3E%3Cpath class='cls-1' d='M67.46,117.49h91.36V83.6H67.46Zm76.32-18.58a4.77,4.77,0,1,1,9.54,0h0V107h-9.54Zm-13.74,0a4.78,4.78,0,0,1,9.55-.44,3.23,3.23,0,0,1,0,.44V107H130Zm-24.83,3.33a7.93,7.93,0,0,1,7-7.86V93.19h2.1v1.22a7.92,7.92,0,0,1,6.79,7.83v13.43h-6.79v1.66h-2.1v-1.66h-7ZM86.7,98.87a4.77,4.77,0,1,1,9.54,0h0V107H86.7Zm-13.7,0a4.77,4.77,0,0,1,9.54,0h0V107H73Z'/%3E%3Cpath class='cls-1' d='M67.46,80.45h91.36V49.7H67.46Zm76.4-14.57a4.78,4.78,0,0,1,9.55,0V74h-9.55Zm-13.73,0a4.77,4.77,0,1,1,9.54,0h0V74h-9.54Zm-10,2.63V59a4.78,4.78,0,0,1,4.79,4.77h0V74H120.1Zm-11.65-9a4.78,4.78,0,0,1,9.56,0V74h-9.56Zm-6.88,4.14a4.78,4.78,0,0,1,4.78-4.78v15H101.6Zm-14.82,2.2a4.78,4.78,0,0,1,9.55,0v8.08H86.78ZM73,65.88a4.78,4.78,0,0,1,9.55,0V74H73Z'/%3E%3Crect class='cls-1' x='112.18' y='115.67' width='2.1' height='1.66'/%3E%3Cpath class='cls-1' d='M114.28,94.41V93.19h-2.1v1.19a8.39,8.39,0,0,1,1-.06A10,10,0,0,1,114.28,94.41Z'/%3E%3Cpath class='cls-1' d='M114.28,115.67V94.41a7.3,7.3,0,0,0-1.14-.09,8.39,8.39,0,0,0-1,.06v21.29Z'/%3E%3Cpolygon class='cls-1' points='113.97 42.44 112.66 42.44 62.49 42.44 62.49 47.33 163.79 47.33 163.79 42.44 113.97 42.44'/%3E%3Crect class='cls-1' x='112.66' y='26.06' width='1.31' height='16.38'/%3E%3Cpolygon class='cls-1' points='123.07 29.89 126.72 26.06 114.93 26.06 114.93 33.72 126.72 33.72 123.07 29.89'/%3E%3Crect class='cls-1' x='105.21' y='119.01' width='15.85' height='2.33'/%3E%3Crect class='cls-1' x='103.39' y='123.44' width='19.51' height='2.33'/%3E%3Crect class='cls-1' x='101.8' y='127.73' width='22.68' height='2.33'/%3E%3Crect class='cls-2' width='226.36' height='164.75'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + background-size: 85%; + + +} + +.boarding_school{ + height: 155px; + background-repeat: no-repeat; + background-position: center; + background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 226.36 163.55'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%236e6e6e;%7D.cls-2%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Cg id='Layer_2' data-name='Layer 2'%3E%3Cg id='Layer_1-2' data-name='Layer 1-2'%3E%3Cpath class='cls-1' d='M132.13,129.13h15.48V59.3l-34.78-19-34.77,19v69.83h26.53m25.94-43.6h8.53V97.34h-8.53ZM86,86.43h8.52v11.8H86Zm15.06,42.69h-8M105,98.23H96.45V86.43H105Zm23.65-.89H120.1V85.53h8.53Zm-6-32.67a9.78,9.78,0,1,1-9.78-9.78h0a9.77,9.77,0,0,1,9.74,9.78Z'/%3E%3Cpolygon class='cls-1' points='72.95 48.8 72.95 57.26 112.83 35.45 152.72 57.26 152.72 48.8 112.83 26.98 72.95 48.8'/%3E%3Cpath class='cls-1' d='M152.87,129.13h58.44V91.44H152.87Zm42.28-32.4h8.53v11.8h-8.53Zm0,16.38h8.53v11.8h-8.53ZM184.72,96.73h8.53v11.8h-8.53Zm0,16.38h8.53v11.8h-8.53ZM170.93,96.73h8.53v11.8h-8.53Zm0,16.38h8.53v11.8h-8.53ZM160.5,96.73H169v11.8h-8.5Zm0,16.38H169v11.8h-8.5Z'/%3E%3Cpath class='cls-1' d='M14.36,129.13H72.79V91.44H14.36Zm42.28-32.4h8.52v11.8H56.64Zm0,16.38h8.52v11.8H56.64ZM46.21,96.73h8.52v11.8H46.21Zm0,16.38h8.52v11.8H46.21ZM32.42,96.73h8.52v11.8H32.42Zm0,16.38h8.52v11.8H32.42ZM22,96.73h8.52v11.8H22Zm0,16.38h8.52v11.8H22Z'/%3E%3Cpolygon class='cls-1' points='14.36 87.92 72.79 87.92 72.79 69.08 31.2 69.08 14.36 87.92'/%3E%3Cpolygon class='cls-1' points='194.46 69.08 152.87 69.08 152.87 87.92 211.31 87.92 194.46 69.08'/%3E%3Cpath class='cls-1' d='M118.26,65h-4.2V58.1h-1.79v8.8h6Z'/%3E%3Crect class='cls-2' width='226.36' height='163.55'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + background-size: 80%; +} + +.boarding_school:hover{ + background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 226.36 163.55'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%23900000;%7D.cls-2%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Cg id='Layer_2' data-name='Layer 2'%3E%3Cg id='Layer_1-2' data-name='Layer 1-2'%3E%3Cpath class='cls-1' d='M132.13,129.13h15.48V59.3l-34.78-19-34.77,19v69.83h26.53m25.94-43.6h8.53V97.34h-8.53ZM86,86.43h8.52v11.8H86Zm15.06,42.69h-8M105,98.23H96.45V86.43H105Zm23.65-.89H120.1V85.53h8.53Zm-6-32.67a9.78,9.78,0,1,1-9.78-9.78h0a9.77,9.77,0,0,1,9.74,9.78Z'/%3E%3Cpolygon class='cls-1' points='72.95 48.8 72.95 57.26 112.83 35.45 152.72 57.26 152.72 48.8 112.83 26.98 72.95 48.8'/%3E%3Cpath class='cls-1' d='M152.87,129.13h58.44V91.44H152.87Zm42.28-32.4h8.53v11.8h-8.53Zm0,16.38h8.53v11.8h-8.53ZM184.72,96.73h8.53v11.8h-8.53Zm0,16.38h8.53v11.8h-8.53ZM170.93,96.73h8.53v11.8h-8.53Zm0,16.38h8.53v11.8h-8.53ZM160.5,96.73H169v11.8h-8.5Zm0,16.38H169v11.8h-8.5Z'/%3E%3Cpath class='cls-1' d='M14.36,129.13H72.79V91.44H14.36Zm42.28-32.4h8.52v11.8H56.64Zm0,16.38h8.52v11.8H56.64ZM46.21,96.73h8.52v11.8H46.21Zm0,16.38h8.52v11.8H46.21ZM32.42,96.73h8.52v11.8H32.42Zm0,16.38h8.52v11.8H32.42ZM22,96.73h8.52v11.8H22Zm0,16.38h8.52v11.8H22Z'/%3E%3Cpolygon class='cls-1' points='14.36 87.92 72.79 87.92 72.79 69.08 31.2 69.08 14.36 87.92'/%3E%3Cpolygon class='cls-1' points='194.46 69.08 152.87 69.08 152.87 87.92 211.31 87.92 194.46 69.08'/%3E%3Cpath class='cls-1' d='M118.26,65h-4.2V58.1h-1.79v8.8h6Z'/%3E%3Crect class='cls-2' width='226.36' height='163.55'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + background-size: 85%; +} + +.college_school{ + background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 226.36 164.75'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%236e6e6e;%7D.cls-2%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Cg id='Layer_2' data-name='Layer 2'%3E%3Cg id='Layer_1-2' data-name='Layer 1-2'%3E%3Cpath class='cls-1' d='M62.72,15.43V22.3H29.36V15.43H23.87V132.81h5.49V99.58h9.33v33.23h2.68V99.58h9.34v33.23h2.68V99.58h9.33v33.23h5.49V15.43Zm-24,80.12H29.36v-17h9.33ZM40,51.15H33.3V37.79c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5Zm2.81-13.36c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5V51.15H42.73Zm8,57.76H41.37v-17h9.34Zm1.45-44.4V37.79c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5V51.15Zm10.56,44.4H53.39v-17h9.33Z'/%3E%3Cpath class='cls-1' d='M197.14,15.43V22.3H163.78V15.43h-5.49V132.81h5.49V99.58h9.34v33.23h2.67V99.58h9.34v33.23h2.68V99.58h9.33v33.23h5.49V15.43Zm-24,80.12H163.8v-17h9.34Zm1.22-44.4h-6.62V37.79c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5Zm2.81-13.36c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5V51.15h-6.62Zm8,57.76h-9.34v-17h9.34Zm1.45-44.4V37.79c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5V51.15Zm10.56,44.4h-9.33v-17h9.33Z'/%3E%3Cpath class='cls-1' d='M69.56,129.87h87.06V100.68H69.56Zm72.73-16a4.57,4.57,0,0,1,9.1,0v7h-9.1Zm-13.1,0a4.57,4.57,0,0,1,9.1,0v7h-9.1Zm-23.66,2.87c0-3.49,2.9-6.36,6.64-6.77v-1h2v1a7.1,7.1,0,0,1,6.47,6.75v11.55h-6.47v1.44h-2v-1.44h-6.64Zm-17.65-2.91a4.57,4.57,0,0,1,9.1,0v7h-9.1Zm-13.09,0a4.57,4.57,0,0,1,9.1,0v7h-9.1Z'/%3E%3Cpath class='cls-1' d='M69.56,98h87.06V71.5H69.56Zm72.81-12.55a4.57,4.57,0,0,1,9.1,0v7h-9.1Zm-13.09,0a4.57,4.57,0,0,1,9.1,0v7h-9.1Zm-9.55,2.26V79.46a4.35,4.35,0,0,1,4.55,4.12v8.81h-4.55ZM108.61,80a4.58,4.58,0,0,1,9.12,0V92.39h-9.12Zm-6.55,3.57a4.34,4.34,0,0,1,4.56-4.12V92.36h-4.56ZM88,85.4a4.57,4.57,0,0,1,9.1,0v7H88Zm-13.1,0a4.57,4.57,0,0,1,9.1,0v7H74.9Z'/%3E%3Crect class='cls-1' x='112.17' y='128.29' width='2' height='1.43'/%3E%3Cpath class='cls-1' d='M114.17,110v-1h-2v1c.3,0,.61-.06.92-.06A9.77,9.77,0,0,1,114.17,110Z'/%3E%3Cpath class='cls-1' d='M114.17,128.29V110a9.81,9.81,0,0,0-1.08-.08,4.69,4.69,0,0,0-.92.06V128.3Z'/%3E%3Cpolygon class='cls-1' points='113.8 67.35 112.68 67.35 69.56 67.35 69.56 69.46 156.62 69.46 156.62 67.35 113.8 67.35'/%3E%3Crect class='cls-1' x='112.63' y='51.15' width='1.24' height='14.1'/%3E%3Cpolygon class='cls-1' points='122.55 54.45 126.03 51.15 114.79 51.15 114.79 57.74 126.03 57.74 122.55 54.45'/%3E%3Crect class='cls-1' x='105.53' y='131.18' width='15.11' height='2'/%3E%3Crect class='cls-1' x='103.79' y='134.98' width='18.59' height='2'/%3E%3Crect class='cls-1' x='102.28' y='138.68' width='21.61' height='2'/%3E%3Crect class='cls-2' width='226.36' height='164.75'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + height: 155px; + background-repeat: no-repeat; + background-position: center; + background-size: 80%; +} + +.college_school:hover{ + background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 226.36 164.75'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%23900000;%7D.cls-2%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Cg id='Layer_2' data-name='Layer 2'%3E%3Cg id='Layer_1-2' data-name='Layer 1-2'%3E%3Cpath class='cls-1' d='M62.72,15.43V22.3H29.36V15.43H23.87V132.81h5.49V99.58h9.33v33.23h2.68V99.58h9.34v33.23h2.68V99.58h9.33v33.23h5.49V15.43Zm-24,80.12H29.36v-17h9.33ZM40,51.15H33.3V37.79c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5Zm2.81-13.36c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5V51.15H42.73Zm8,57.76H41.37v-17h9.34Zm1.45-44.4V37.79c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5V51.15Zm10.56,44.4H53.39v-17h9.33Z'/%3E%3Cpath class='cls-1' d='M197.14,15.43V22.3H163.78V15.43h-5.49V132.81h5.49V99.58h9.34v33.23h2.67V99.58h9.34v33.23h2.68V99.58h9.33v33.23h5.49V15.43Zm-24,80.12H163.8v-17h9.34Zm1.22-44.4h-6.62V37.79c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5Zm2.81-13.36c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5V51.15h-6.62Zm8,57.76h-9.34v-17h9.34Zm1.45-44.4V37.79c0-2.75,1.48-5,3.31-5s3.31,2.22,3.31,5V51.15Zm10.56,44.4h-9.33v-17h9.33Z'/%3E%3Cpath class='cls-1' d='M69.56,129.87h87.06V100.68H69.56Zm72.73-16a4.57,4.57,0,0,1,9.1,0v7h-9.1Zm-13.1,0a4.57,4.57,0,0,1,9.1,0v7h-9.1Zm-23.66,2.87c0-3.49,2.9-6.36,6.64-6.77v-1h2v1a7.1,7.1,0,0,1,6.47,6.75v11.55h-6.47v1.44h-2v-1.44h-6.64Zm-17.65-2.91a4.57,4.57,0,0,1,9.1,0v7h-9.1Zm-13.09,0a4.57,4.57,0,0,1,9.1,0v7h-9.1Z'/%3E%3Cpath class='cls-1' d='M69.56,98h87.06V71.5H69.56Zm72.81-12.55a4.57,4.57,0,0,1,9.1,0v7h-9.1Zm-13.09,0a4.57,4.57,0,0,1,9.1,0v7h-9.1Zm-9.55,2.26V79.46a4.35,4.35,0,0,1,4.55,4.12v8.81h-4.55ZM108.61,80a4.58,4.58,0,0,1,9.12,0V92.39h-9.12Zm-6.55,3.57a4.34,4.34,0,0,1,4.56-4.12V92.36h-4.56ZM88,85.4a4.57,4.57,0,0,1,9.1,0v7H88Zm-13.1,0a4.57,4.57,0,0,1,9.1,0v7H74.9Z'/%3E%3Crect class='cls-1' x='112.17' y='128.29' width='2' height='1.43'/%3E%3Cpath class='cls-1' d='M114.17,110v-1h-2v1c.3,0,.61-.06.92-.06A9.77,9.77,0,0,1,114.17,110Z'/%3E%3Cpath class='cls-1' d='M114.17,128.29V110a9.81,9.81,0,0,0-1.08-.08,4.69,4.69,0,0,0-.92.06V128.3Z'/%3E%3Cpolygon class='cls-1' points='113.8 67.35 112.68 67.35 69.56 67.35 69.56 69.46 156.62 69.46 156.62 67.35 113.8 67.35'/%3E%3Crect class='cls-1' x='112.63' y='51.15' width='1.24' height='14.1'/%3E%3Cpolygon class='cls-1' points='122.55 54.45 126.03 51.15 114.79 51.15 114.79 57.74 126.03 57.74 122.55 54.45'/%3E%3Crect class='cls-1' x='105.53' y='131.18' width='15.11' height='2'/%3E%3Crect class='cls-1' x='103.79' y='134.98' width='18.59' height='2'/%3E%3Crect class='cls-1' x='102.28' y='138.68' width='21.61' height='2'/%3E%3Crect class='cls-2' width='226.36' height='164.75'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + background-size: 85%; +} + +#school_icon:hover{ +fill: #900000; + + } + +.title-rside{ + display:none; +} + + + +h2.landing-body{ + margin-bottom: 0.25em; + font-size: 3.667em; + font-weight: 200; + letter-spacing: 1px; + text-align: center; + text-transform: uppercase; + color: #900; + margin-bottom: .5em; +} + +h3.landing-body-copy{ + font-size: 1.9em; + color: #595959; + line-height: 1.6em; + font-weight: 300; + padding-bottom: .2em; + text-align: center; + +} + +a{ + color: #900000; +} + +.profile_questions{ + font-size: 1.25rem; + font-weight: normal; + line-height: 1.75em; + font-family: "Source Sans Pro"; +} + + + + + +/*------------------------Input Styling---------------------------*/ + .schoolMetrics{ + margin: auto; + width: 100%; + max-width: 700px; + text-align:center; + + } +.schoolMetrics label{ + color: #f0f0f0; +} + +.btn-continue{ + display: inline-block; + padding: 0.75em 1.5em; + background-color: #990011; + border-radius: 0; + color: #fff; + cursor: pointer; + text-decoration: none; + font-family: "Source Sans Pro"; + font-size: 2rem; + font-weight: 800; + letter-spacing: 1px; + text-transform: uppercase; + white-space: inherit; + border: 3px solid #990011; + line-height: 1.25; + text-align: center; + vertical-align: middle; + transition: all .2s ease-in-out; + -webkit-appearance: button; +} + + + +.profile-input{ + padding-bottom: 3%; +} + + + +.downloader-cache { + text-align: center; +} + +/*section.sidebar .shiny-bound-input.action-button, section.sidebar .shiny-bound-input.action-link{*/ + .opt-button { + float: right; + margin-top: 10px; + background-color: transparent; + border: none; + } + + .profiler-button { + max-width:50%; + float: left; + } + + .sidebar-menu>li { + display: none; + } + + h3.zip{ + padding-bottom: 5%; + } + h2.zip{ + padding-bottom: 2%; + } + + .zip-box{ + padding: 0 10% 0 10%; + } + + div.zip-upload.form-group.shiny-input-container:not(.shiny-input-container-inline){ + + padding: 0; + } \ No newline at end of file diff --git a/www/brand_style.css b/www/brand_style.css new file mode 100644 index 00000000..e8043bb0 --- /dev/null +++ b/www/brand_style.css @@ -0,0 +1,415 @@ +/*----------------------------------------------------------------------------------*/ + /*--------------------------------TEMPLATE STYLING----------------------------------*/ + /*----------------------------------------------------------------------------------*/ + /* The below code should not be edited, except for when changes need to be made to the overall template. If you need to edit this code, + + +/*------------------------------------------------*/ + /*-----------------FONT STYLING-------------------*/ + /*------------------------------------------------*/ + + h1 { + margin: 25px; + text-align:center; + font-family: Source Sans Pro; + font-weight: 300; + font-size: 2.0em; + color: #222222; + text-decoration: none solid rgb(158, 162, 162); + line-height: 80px; + } + +h2 { + margin: 0; + padding: 0; + font-family: "Source Sans Pro", sans-serif; +} + + + +/*h2 { + font-family: Source Sans Pro; + font-size: 30px; + color: #ab2328; + text-decoration: none solid rgb(171, 35, 40); + text-transform: uppercase; + text-align: center; +}*/ + + /*h3 { + line-height: 1.5em; + font-size: 1.3em; + font-family: "Source Sans Pro", sans-serif; + text-decoration: none solid rgb(0, 32, 91); + color: #00205b; + text-transform: uppercase; + }*/ + + h3{ + margin: 0; + padding: 0; + font-family: "Source Sans Pro", sans-serif; + } + +h4 { + font-family: Source Sans Pro; + font-size: 30px; + color: #ab2328; + text-decoration: none solid rgb(171, 35, 40); + text-transform: uppercase; + text-align: center; +} + +#lastupdate{ +display:inline; +vertical-align:middle; +float:right; +} + +#title{ +display:inline; +vertical-align:middle; +} + +p { + padding-bottom: 6.9px; + font-size: 1.2em; + line-height: 1.6em; + font-weight: 400; + color: #6e6e6e; +} + + +a{ + color: #900000; +} +/*------------------------------------------------*/ + + + /*------------------------------------------------*/ + /*---------------STRUCTURE STYLING----------------*/ + /*------------------------------------------------*/ + + .page_title { + padding-left: 2rem; + } + + +.main-header{ + padding-top: 5px; + position: fixed; + width: 100%; + + background: white; +} + + +/*---------------ELEMENT STYLING------------------*/ + + + img { + max-width:100%; + max-height:100%; + height:auto; + width: 100%; + } + +/*------------------------------------------------*/ + + + + .header{ + text-align: center; + color: #990000; + padding-bottom: 20px; + + } + + .shiny-input-container:not(.shiny-input-container-inline){ + width: 100%; + /*max-width: 300px;*/ + /*padding: 0 13% 0 13%;*/ +} + + + + + /*---------------NAV BAR STYLING------------------*/ + + + .navbar-default .navbar-nav .active a { + font-weight: 400; + background-color: #fff; + color: #222; + padding: 13px 10px; + } + +.navbar-default .navbar-nav li a { + font-weight: 400; + background-color: #fff; + color: #222; + padding: 13px 10px; +} + +.navbar-default .navbar-nav li a:hover { + background-color: #e7e7e7; +} + +.navbar-brand { + padding: 0; + margin: 0; +} + +.navbar-default { + background-color: white; + border-color: white; + padding: 5px 10px; +} + +.navbar-nav .li a { + padding-left:10px; + padding-right:10px; +} + + +#header_logo { +height: auto; +width: 13%; + +} + +.title-text { + font-family: Source Sans Pro; + + color: #222222; + text-decoration: none solid rgb(34, 34, 34); + text-align: left; + display: inline-block; + vertical-align: middle; + white-space:nowrap; + line-height: 100%; + padding-right: 3%; + margin-right: 1%; +} + +.title-rside { + float:right; + +} + + +.logo-text{ + display: inline; + font-size: 1.2em; + vertical-align: middle; +} +/*------------------------------------------------*/ + + + /*------------------------------------------------*/ + /*--------------DASHBOARD STYLING-----------------*/ + /*------------------------------------------------*/ + + .sidebar{ + color: #343434; + margin-top: 25px; + width: inherit; + position: fixed; + display:inline-block; + } + + + + + .img-wrapper { + padding: 10px; + } + +.side_sub { + max-height:85vh; + max-width: 300px; + overflow:auto; + padding-top:10px; +} + + + + +/*------------------------------------------------*/ + /*---------------SKIN OVERWRITING-----------------*/ + /*------------------------------------------------*/ + /* For some reason there is an issue with the shinydashboard skin styles overwriting all +other styles, so these are going to look super specific and dumb but I don't know a better way of doing it */ + + .skin-black .left-side, .skin-black .main-sidebar, .skin-black .wrapper { + background-color: #f0f0f0; + } + + .skin-black .main-header>.logo { + border-right:0; + } + + .skin-black .main-header .navbar>.sidebar-toggle { + border-right:0; + } + .sidebar-toggle{ + /* position: absolute; + z-index: 5; + border-right: 1px solid #eee; + font-family: fontAwesome, 'Font Awesome 5 Free'; + background: white; + padding: 15px; + + + color: #9ea2a2; + + font-weight: 900; + */ + height: 100%; + /*font-size: 20px;*/ + padding-left:0 !important; + display: inline-block; + + } + + + + .skin-black .sidebar a { + background: #fff; + color: #343434; + font-size: 1.1em; + } + + .skin-black .sidebar-menu>li:hover>a { + background: #f7f7f7; + color: #343434; + } + + .skin-black .sidebar-menu>li.active>a { + background: #f7f7f7; + color: #343434; + } + + .skin-black .sidebar-menu>li>.treeview-menu{ + background:#fff; + } + + + #sidebarCollapsed { + border: solid 1px #f7f7f7; + position:fixed; + height: inherit; + background: white; + } + /*------------------------------------------------*/ + + + /*------------------------------------------------*/ + /*-----------------INPUT STYLING------------------*/ + /*------------------------------------------------*/ + .selectize-input { + background: #FAFAFA; + font-family: SourceSansPro-Regular; + max-width: 100%; + font-size: 15px; + color: #333366; + letter-spacing: 0; + border: none; + } + + .file-input { + max-width:100%; + } + .options{ + display: inline-block; + position: relative; + float: right; + vertical-align: bottom; + } + + .control-label{ + font-weight: 400; + } + + + + /*---slider styling---*/ + .irs-slider { + top: 17px !important; + width: 22px !important; + height: 22px !important; + border: 1px solid #c35442 !important; + background: #ab2328 !important; + border-radius: 27px !important; + -moz-border-radius: 27px !important; + box-shadow: 1px 1px 3px rgba(0,0,0,0.3) !important; + cursor: pointer !important; + } + +.irs-bar { + height: 8px !important; + top: 25px !important; + border-top: 1px solid #990000 !important; + border-bottom: 1px solid #990000 !important; + background: #990000 !important; +} + +.irs-from, .irs-to, .irs-single { + color: #fff !important; + font-size: 11px !important; + line-height: 1.333 !important; + text-shadow: none !important; + padding: 1px 3px !important; + background: #343434 !important; + border-radius: 3px !important; + -moz-border-radius: 3px !important; + +} + +.sidebar .irs-min, .sidebar .irs-max{ + + color: #343434; +} + +.irs-bar-edge{ + background: #990000 !important; + border: 1px solid #990000 !important; +} + +.irs { + max-width: 100% !important; +} + +/*---------------------------*/ + + + + .options .shiny-input-container:not(.shiny-input-container-inline) { + width: 100%; + padding: 8% 7% 2% 7%; + } + +.options { + position: fixed; + /* float: right; */ + /* vertical-align: bottom; */ + background-color: white; + z-index: 4; + /*padding: %;*/ + width: 100%; + max-width: 400px; + padding-left: 0; + height: 100%; + max-height: 500px; + overflow: scroll; +} + /*-------------------------------------------------*/ + + +/*------------------------------------------------*/ + +/*------------------------------------------------*/ + + + + diff --git a/www/contact_style.css b/www/contact_style.css new file mode 100644 index 00000000..e69de29b diff --git a/www/minder_style.css b/www/minder_style.css new file mode 100644 index 00000000..81914481 --- /dev/null +++ b/www/minder_style.css @@ -0,0 +1,192 @@ + + +/*------------------------------------------------*/ +/*-------------------COVID Minder-----------------*/ +/*------------------------------------------------*/ +.map-title { + width: 80%; + z-index: 10; + position: absolute; + left: 50%; + top: 10px; + transform: translateX(-50%); +} + +.NY_case_plots { + height: Calc(90% - 65px); +} + + + +.select-bar { + position:absolute; + right:25px; + margin-top: 60px; + z-index: 999; +} + +.leaflet-container { + background:none; +} + +.dropdown-toggle > div:first-child { + display: inline-block; +} + + + +.leaflet-top { + z-index: 999; +} + +.shiny-options-group { + padding-left:10px; +} + +.content-wrapper{ + background-color: #f0f0f0; +} + +.content{ + background-color: #f5f5f5; + padding: 0; +} + +.tab-content { + min-height: 75vh; + padding-right: 1%; + width: 100%; + background-color: #f0f0f0; + float: right; + + } + +@media screen and (min-width:768px){ +.map-container { + display: flex; + justify-content: center; + align-items: center; +} + +} + +@media screen and (min-width:1085px){ + .navbar-nav { + float: right; +} + +.content-wrapper{ + padding-top: 90px; +} + +} + +@media screen and (min-width:1600px) { + html, body, p, .input-sm { + font-size: 16px; +} +.main-header{ + position: fixed; +} +.content-wrapper{ + padding-top: 90px; +} +} + + +@media screen and (max-width:1160px){ + .title-text { + /*height: 50px;*/ + } + .NY_case_plots { + height: Calc(90% - 140px); +} +.main-header{ + position: fixed; +} +.content-wrapper{ + padding-top: 90px; +} + +} + + +@media (max-width: 767px){ +.skin-black .main-header>.logo { + background-color: #f7f7f7; + height:10vh; + +} +#header_logo { + width: 10%; + display: inline; + vertical-align: middle; +} + + +.main-header { + position: fixed; + padding-top: 15px; + /* padding-bottom: 11px; */ + width: 100%; + /* padding-right: 2%; */ + background: #f7f7f7; +} + +.title-text{ + text-align:center; +} + + + +.main-header .sidebar-toggle { + float: left; + background-color: transparent; + background-image: none; + padding: 4px 15px; + float: left; + margin-top: -60px; + margin-left: 15px; + background: #f7f7f7; + padding-left: 5px; + padding-bottom: 0; +} + +.main-header .sidebar-toggle { +padding-top: 6px !important; + +} + +.title-rside{ + display: none; +} + +.main-header .navbar{ + min-height: 0; +} + +h1{ + line-height: 2.5em; +} + +#lastupdate{ + display: table; + vertical-align: middle; + float: left; + margin-top: 0; + margin-left: 3%; + +} + +#title{ + display:table; + vertical-align:middle; + line-height: 2.5em; + margin-bottom: 0; + margin-left: 3%; + + margin-right: 21px; +} + + +} \ No newline at end of file diff --git a/www/mockup_style.css b/www/mockup_style.css new file mode 100644 index 00000000..a788f80a --- /dev/null +++ b/www/mockup_style.css @@ -0,0 +1,32 @@ +/*------------------------------------------------*/ + /*----------------MOCKUP STYLING------------------*/ + /*------------------------------------------------*/ + + + + .tab-title { + text-align: center; + color:#343434; + padding:-5px; + } + +.leaflet-container { + height:100%; +} + +.gg_tooltip { + background-color: #f7f7f7; + background-color: rgba(245, 245, 245, 0.85); + padding: 0 10px; + margin: 10px 0; + line-height: 1.5em; +} + +.footer { + text-align: center; + margin-bottom: 20px; + position: absolute; + width:100%; +} +/*------------------------------------------------*/ + \ No newline at end of file diff --git a/www/twitter.css b/www/twitter.css new file mode 100644 index 00000000..e69de29b diff --git a/www/warroom_style.css b/www/warroom_style.css new file mode 100644 index 00000000..6961cee0 --- /dev/null +++ b/www/warroom_style.css @@ -0,0 +1,47 @@ + + +/*------------------------------------------------*/ + /*-------------------COVID WarRoom-----------------*/ + /*------------------------------------------------*/ + + .box-header { + border-top-left-radius: 7px; + border-top-right-radius: 7px; + background: #B4B4B4; + text-align: center; + } +.box-title { + font-family: Source Sans Pro; + text-decoration: none solid rgb(240, 240, 240); + text-transform: uppercase; + color: white; +} +#box-header{ +color: white; +font-size: 2.0rem; +line-height: 2.4rem; +} + +.box{ + border-top: none; +} +.mid-col { + height: auto; + padding: 6%; + background-color: white; + +} + +.mid-col-subhead { + + height:auto; + margin:auto; + background: #7fa9ae; + font-family: Source Sans Pro; + + color: #f0f0f0; + text-decoration: none solid rgb(240, 240, 240); + text-transform: uppercase; + text-align: center; + +}