I am trying to build a shiny app that has the following feature:
It has a parent-child feature that can expand and collapse as per
user interaction (Requirement 1 - Done)
When the rows are expanded, several child rows are displayed in the
data table. I want to introduce multiple scroll bars in this table.
1st scrollbar will be for 1st 4 columns and another scrollbar for the
rest of the columns. (Requirement 2 - Not Done)
The below code is able to produce results for the 1st requirement (using JQuery) however, I am unable to find a way out for Requirement 2.
Can anyone assist here?
packages = c(
'shiny',
'shinydashboard',
'tidyverse',
'dplyr',
'magrittr',
'plotly',
'ggplot2',
'scales',
'DT',
"shinyWidgets",
"fontawesome"
)
for (p in packages) {
if (!require(p, character.only = T)) {
install.packages(p)
}
library(p, character.only = T)
}
DataIn <- mtcars
DataIn <- DataIn %>% tidyr::nest(-cyl)
DataIn <- DataIn %>%
{
bind_cols(data_frame(
' ' = rep(
'<img src=\"https://raw.githubusercontent.com/DataTables/DataTables/master/examples/resources/details_open.png\"/>',
nrow(.)
)
), .)
}
# get dynamic info and strings
nested_columns <-
which(sapply(DataIn, class) == "list") %>% setNames(NULL)
not_nested_columns <-
which(!(seq_along(DataIn) %in% c(1, nested_columns)))
not_nested_columns_str <-
not_nested_columns %>% paste(collapse = "] + '_' + d[") %>% paste0("d[", ., "]")
CallBack <- paste0(
"
table.column(1).nodes().to$().css({cursor: 'pointer'});
// Format data object (the nested table) into another table
var format = function(d) {
if(d != null){
var result = ('<table id=\"child_' + ",
not_nested_columns_str,
" + '\">') + '<thead><tr>'
for (var col in d[",
nested_columns,
"][0]){
result += '<th>' + col + '</th>'
}
result += '</tr></thead></table>'
return result
}else{
return '';
}
}
var format_datatable = function(d) {
var dataset = [];
for (i = 0; i <= d[",
nested_columns,
"].length-1; i++) {
var datarow = $.map(d[",
nested_columns,
"][i], function(value, index) {
return [value];
});
dataset.push(datarow);
}
var subtable = $(('table#child_' + ",
not_nested_columns_str,
")).DataTable({
'data': dataset,
'autoWidth': true,
'deferRender': true,
'info': false,
'lengthChange': false,
'ordering': true,
'paging': false,
'scrollX': false,
'scrollY': false,
'searching': false
// 'fnRowCallback': function (nRow, aData, iDisplayIndex, iDisplayIndexFull) {
// $('td', nRow).css('background-color', 'Red')}
});
};
table.on('click', 'td.details-control', function() {
var td = $(this), row = table.row(td.closest('tr'));
if (row.child.isShown()) {
row.child.hide();
td.html('<img src=\"https://raw.githubusercontent.com/DataTables/DataTables/master/examples/resources/details_open.png\"/>');
} else {
row.child(format(row.data())).show();
td.html('<img src=\"https://raw.githubusercontent.com/DataTables/DataTables/master/examples/resources/details_close.png\"/>');
format_datatable(row.data())
}
});"
)
shinyApp(
ui = fluidPage(DT::dataTableOutput('tbl')),
server = function(input, output) {
output$tbl = DT::renderDataTable(datatable(
DataIn,
escape = -2,
# raw HTML in column 2
options = list(columnDefs = list(
list(visible = FALSE, targets = c(0, nested_columns)),
# Hide row numbers and nested columns
list(
orderable = FALSE,
className = 'details-control',
targets = 1
) # turn first column into control column
)),
callback = JS(CallBack)
))
}
)
Related
I am interested in incorporating a simple, radio button-style quiz in a bookdown::bs4_book environment. I have a minimal example below that works when I specify bookdown::gitbook in the header, but stops working when I choose bookdown::bs4_book. Does anyone have any ideas how to get this working with the bookdown::bs4_book option?
(note: the .Rmd code below needs to be saved as "index.Rmd" in order to be knitted using bs4_book.)
Thank you,
Luke
---
title: "formative_test"
site: bookdown::bookdown_site
output:
# bookdown::gitbook:
bookdown::bs4_book:
css: [style.css, font-awesome.min.css]
repo: https://github.com/rstudio/bookdown-demo
---
# Chapter 1
```{r results = "asis", echo = FALSE}
question <- function(question, distractors, correct, no, fb = "", print_question = TRUE){
allanswers <- c(distractors, correct)[sample.int(length(distractors)+1)]
correctanswer <- which(allanswers == correct)
answercode <- paste0(sapply(1:length(allanswers), function(i){
x <- allanswers[i]
paste0('<div class="radio">\n <label>\n <input type="radio" name="question', no, '" id="opt', i,'" value="', i, '" onchange="check_answer', no, '()">\n ', x, '\n </label>\n</div>')
}), collapse = "\n\n")
out <- paste0(question, "\n\n", answercode,
'<div class="collapse" id="collapseExample', no,'">
<div class="card card-body" id="answerFeedback', no, '">
</div>
</div>',
paste0('<script type="text/javascript">
function check_answer', no, '()
{
var radioButtons', no, ' = document.getElementsByName("question', no, '");
document.getElementById("answerFeedback', no, '").innerHTML = "Try selecting an answer!!";
for(var i = 0; i < radioButtons', no, '.length; i++)
{
if(radioButtons', no, '[i].checked == true)
{
var feedback', no, ' = "<p style=\'color:red\'>Wrong', ifelse(fb == "", ".", paste0("; ", fb)), '</p>";
if(radioButtons', no, '[i].value == "', correctanswer, '") {
feedback', no, ' = "<p style=\'color:green\'>Correct!</p>"
}
document.getElementById("answerFeedback', no, '").innerHTML = feedback', no, ';
return true;
}
}
}
</script>
'))
if(print_question){
cat(out)
} else {
return(out)
}
}
questionnaire <- function(x, shuffle = TRUE, print_question = TRUE){
if(inherits(x, "character")) x <- read.csv(x, stringsAsFactors = FALSE, fileEncoding="UTF-8-BOM")
if(!all(names(x) == c("question", "distractors", "correct", "fb"))){
stop("Incorrect column names")
}
if(shuffle){
x <- x[sample.int(nrow(x)), ]
}
out <- ""
for(i in 1:nrow(x)){
out <- paste0(out,
"**Question ",
i,
":**\n",
question(question = x$question[i],
distractors = eval(parse(text = x$distractors[i])),
correct = x$correct[i],
no = i,
fb = ifelse(is.na(x$fb[i]), "", x$fb[i]),
print_question = FALSE),
"\n\n"
)
}
if(print_question){
cat(out)
} else {
return(out)
}
}
questionnaire(
x = data.frame(
question = "True or False?",
distractors = "\"FALSE\"",
correct = TRUE,
fb = "here is some example feedback"
)
)
In this DT example with child rows, how to start the table with all the child rows expanded?
library(DT)
datatable(
cbind(' ' = '⊕', mtcars), escape = -2,
options = list(
columnDefs = list(
list(visible = FALSE, targets = c(0, 2, 3)),
list(orderable = FALSE, className = 'details-control', targets = 1)
)
),
callback = JS("
table.column(1).nodes().to$().css({cursor: 'pointer'});
var format = function(d) {
return '<div style=\"background-color:#eee; padding: .5em;\"> Model: ' +
d[0] + ', mpg: ' + d[2] + ', cyl: ' + d[3] + '</div>';
};
table.on('click', 'td.details-control', function() {
var td = $(this), row = table.row(td.closest('tr'));
if (row.child.isShown()) {
row.child.hide();
td.html('⊕');
} else {
row.child(format(row.data())).show();
td.html('⊖');
}
});"
))
PS: stackoverflow forced me to include more details to the question but there is nothing else to add...
You can use your existing callback to also iterate over each row in the table. In that iteration you can create and open each child record:
table.rows().every( function () {
this.child( format(this.data()) ).show();
} );
This snippet needs to be appended to the end of your callback = JS(...) option as shown below:
callback = JS(
"
table.column(1).nodes().to$().css({cursor: 'pointer'});
var format = function(d) {
return '<div style=\"background-color:#eee; padding: .5em;\"> Model: ' +
d[0] + ', mpg: ' + d[2] + ', cyl: ' + d[3] + '</div>';
};
table.on('click', 'td.details-control', function() {
var td = $(this), row = table.row(td.closest('tr'));
if (row.child.isShown()) {
row.child.hide();
td.html('⊕');
} else {
row.child(format(row.data())).show();
td.html('⊖');
}
});
table.rows().every( function () {
this.child( format(this.data()) ).show();
} );"
)
The result:
I'm trying to observe changes to css using javascript mutationObserver in Shiny. I'm using rhandsontable because we can change the width of a table element in the app, and I'm trying to pick up this change iwth the mutationObserver.
The javascript doesn't seem to be working. I'm unsure why. Nothing is logged to the console, no alert message, and shiny doesn't register the variable being set by javascript.
MutationObserver code
jsCode <- "
const observer = new MutationObserver(
# this function runs when something is observed.
function(mutations){
console.log('activated')
var i;
var text;
var widthArray = [];
text = ''
for (i = 0; i < document.getElementsByClassName('htCore')[0].getElementsByTagName('col').length; i++) {
text += document.getElementsByClassName('htCore')[0].getElementsByTagName('col')[i].style.width + '<br>';
widthArray.push(document.getElementsByClassName('htCore')[0].getElementsByTagName('col')[i].style.width);
}
alert(text)
Shiny.setInputValue('colWidth', widthArray);
}
)
const cols = document.getElementsByClassName('htCore')[0].getElementsByTagName('col')
observer.observe(cols, {
attributes: true # observe when attributes of ul.bears change (width, height)
})
"
Shiny code:
library(shiny)
library(rhandsontable)
ui <- fluidPage(
tags$head(tags$script(HTML(jsCode))),
rhandsontable::rHandsontableOutput("dataTable")
)
server <- function(input, output, session) {
df = data.frame(
company = c('a', 'b', 'c', 'd'),
bond = c(0.2, 1, 0.3, 0),
equity = c(0.7, 0, 0.5, 1),
cash = c(0.1, 0, 0.2, 0),
stringsAsFactors = FALSE
)
output$dataTable <- renderRHandsontable({
rhandsontable(df, manualColumnResize = TRUE, manualRowResize = TRUE)
})
observeEvent(input$colWidth, {
print(input$colWidth)
})
}
shinyApp(ui, server)
This works:
jsCode <- "
$(document).on('shiny:connected', function(){
setTimeout(function(){
const observer = new MutationObserver(
function(mutations){
console.log('activated')
var i;
var text;
var widthArray = [];
text = ''
for (i = 0; i < document.getElementsByClassName('htCore')[0].getElementsByTagName('col').length; i++) {
text += document.getElementsByClassName('htCore')[0].getElementsByTagName('col')[i].style.width + '<br>';
widthArray.push(document.getElementsByClassName('htCore')[0].getElementsByTagName('col')[i].style.width);
}
alert(text)
Shiny.setInputValue('colWidth', widthArray);
}
)
const cols = document.getElementsByClassName('htCore')[0].getElementsByTagName('colgroup')[0]
observer.observe(cols, {
attributes: true, subtree: true
});
}, 500);
});
"
I am having an issue selecting the child rows in a R Shiny DT Table with JS callback.
When expanding the parent row, I try to select the child rows, and all rows are selected in that child (including child's background).
If I select 2nd child row, the background is deselcted and it shows my 2 childs selected (every other click selects all child rows, then shows ones selected repeatedly)
Also, how to get the information on which child rows are selected?
Thank you very much!
Alex B
I am trying to play with the datatable settings in the JS callback.
'''
library(data.table)
library(DT)
library(shiny)
library(jsonlite)
ui <- fluidPage(DT::dataTableOutput(width = "100%", "table"))
server <- function(input, output) {
output$table = DT::renderDataTable({
mtcars_dt = data.table(mtcars)
setkey(mtcars_dt,mpg,cyl)
mpg_dt = unique(mtcars_dt[, list(mpg, cyl)])
setkey(mpg_dt, mpg, cyl)
cyl_dt = unique(mtcars_dt[, list(cyl)])
setkey(cyl_dt, cyl)
mtcars_dt = mtcars_dt[, toJSON(.SD), by = list(mpg,cyl)]
setnames(mtcars_dt,'V1','mtcars')
mtcars_dt[, ' ' := '►']
df1 = mtcars_dt
df1 = df1[c(1,6),]
setcolorder(df1, c(length(df1),c(1:(length(df1) - 1))))
DT::datatable(
data = df1,
rownames = FALSE,
escape = FALSE,
selection="multiple",
options = list(
# dom = 'Bfrti',
stripeClasses = list(),
deferRender = TRUE,
# scrollX = TRUE,
pageLength = 25,
scrollY = "1000",
scroller = TRUE,
scollCollapse = TRUE,
lengthMenu = c(20, 50, 100, 500),
searchHighlight = TRUE,
tabIndex = 1,
columnDefs = list(
list(orderable = FALSE, className = 'details-control', targets = 0),
list(visible = FALSE, targets = -1 )
)
),
callback = JS("
//table.header().to$().css({'background-color': '#000', 'color': '#fff'})
table.column(01).nodes().to$().css({cursor: 'pointer'})
var table_id = 1000
// Format child object into another table
var format = function(table_id, columns) {
if(columns != null){
var result = ('<table id=\"' + table_id + '\"><thead><tr>')
for (var i in columns){
result += '<th>' + columns[i] + '</th>'
}
result += '</tr></thead></table>'
return result
}else{
return ''
}
}
var format_datatable = function( table_id, newtable, columns) {
if(newtable != null){
var column_defs = []
for (var i in columns)
{
if (i == 0)
{
column_defs[i] = {'data': columns[i], 'targets': parseInt(i), 'orderable': false, 'className': 'details-control'}
}
else
{
column_defs[i] = {'data': columns[i], 'targets': parseInt(i)}
}
}
/* alert(JSON.stringify(column_defs)) */
//var printTable = document.getElementById(newtable)
//document.write(newtable)
//document.write(columns)
var subtable = $(('table#' + table_id)).DataTable({
'data': newtable,
'autoWidth': false,
'deferRender': true,
'stripeClasses': [],
'info': false,
'select': { style: 'os',
},
'lengthChange': false,
'ordering': false,
'paging': false,
'scrollX': false,
'scrollY': false,
'searching': false,
'columnDefs': column_defs
}).draw()
}
}
table.on('click', 'td.details-control', function() {
var td = $(this)
var table = $(td).closest('table')
var row = $(table).DataTable().row(td.closest('tr'))
if (row.child.isShown()) {
row.child.hide()
td.html('►')
}
else
{
var row_data = row.data()
if (!Array.isArray(row_data))
{
row_data = Object.keys(row_data).map(function (key) {
return row_data[key]
});
}
var newtable = JSON.parse(row_data[row_data.length-1])
var columns = Object.keys(newtable[0])
table_id++
row.child(format(table_id, columns)).show()
format_datatable(table_id, newtable, columns)
console.log(table_id)
td.html('▼')
}
})
")
)
})
observe({
print(input$table_rows_selected)
print(input$newtable_rows_selected)
})
}
shinyApp(ui = ui, server = server)
'''
I would like to highlight individual child rows and know which child rows are selected. Currently it highlights all child rows each time it clicks.
Here is an attempt. This works, but the selection on the main table is disabled.
library(data.table)
library(DT)
library(shiny)
library(jsonlite)
initComplete <- paste(
"function(settings){",
" var table = settings.oInstance.api();",
" var tbl = table.table().node();",
" var id = $(tbl).closest('.dataTable').attr('id');",
" table.on('click', 'tbody tr', function(){",
" // send selected columns to Shiny",
" setTimeout(function(){",
" var indexes = table.rows({selected:true}).indexes();",
" var indices = Array(indexes.length);",
" for(var i = 0; i < indices.length; ++i){",
" indices[i] = indexes[i];",
" }",
" Shiny.setInputValue('childrow_rows_selected', {child: id, rows: indices});",
" },0);",
" });",
"}",
sep = "\n"
)
ui <- fluidPage(DT::dataTableOutput(width = "100%", "table"))
server <- function(input, output) {
output$table = DT::renderDataTable({
mtcars_dt = data.table(mtcars)
setkey(mtcars_dt,mpg,cyl)
mpg_dt = unique(mtcars_dt[, list(mpg, cyl)])
setkey(mpg_dt, mpg, cyl)
cyl_dt = unique(mtcars_dt[, list(cyl)])
setkey(cyl_dt, cyl)
mtcars_dt = mtcars_dt[, toJSON(.SD), by = list(mpg,cyl)]
setnames(mtcars_dt,'V1','mtcars')
mtcars_dt[, ' ' := '►']
df1 = mtcars_dt
df1 = df1[c(1,6),]
setcolorder(df1, c(length(df1),c(1:(length(df1) - 1))))
DT::datatable(
data = df1,
rownames = FALSE,
escape = FALSE,
selection = "none",
extensions = "Select",
options = list(
# dom = 'Bfrti',
stripeClasses = list(),
deferRender = TRUE,
# scrollX = TRUE,
pageLength = 25,
scrollY = "1000",
scroller = TRUE,
scollCollapse = TRUE,
lengthMenu = c(20, 50, 100, 500),
searchHighlight = TRUE,
tabIndex = 1,
columnDefs = list(
list(orderable = FALSE, className = 'details-control', targets = 0),
list(visible = FALSE, targets = -1 )
)
),
callback = JS("
table.column(0).nodes().to$().css({cursor: 'pointer'});
// var table_id = 1000
// Format child object into another table
var format = function(table_id, columns) {
if(columns != null){
var result = ('<table id=\"' + table_id + '\"><thead><tr>')
for (var i in columns){
result += '<th>' + columns[i] + '</th>'
}
result += '</tr></thead></table>'
return result
}else{
return ''
}
}
var format_datatable = function( table_id, newtable, columns) {
if(newtable != null){
var column_defs = []
for (var i in columns)
{
if (i == 0)
{
column_defs[i] = {'data': columns[i], 'targets': parseInt(i), 'orderable': false, 'className': 'details-control'}
}
else
{
column_defs[i] = {'data': columns[i], 'targets': parseInt(i)}
}
}
var subtable = $(('table#' + table_id)).DataTable({
'data': newtable,",
sprintf("initComplete: %s,", initComplete),
" 'autoWidth': false,
'deferRender': true,
'stripeClasses': [],
'info': false,
'select': {style: 'multi'},
'lengthChange': false,
'ordering': false,
'paging': false,
'scrollX': false,
'scrollY': false,
'searching': false,
'columnDefs': column_defs
}).draw()
}
}
table.on('click', 'td.details-control', function() {
var td = $(this);
var table = $(td).closest('table');
var row = $(table).DataTable().row(td.closest('tr'));
var table_id = 'child' + row.index();
if (row.child.isShown()) {
row.child.hide();
td.html('►');
}
else
{
var row_data = row.data();
if (!Array.isArray(row_data))
{
row_data = Object.keys(row_data).map(function (key) {
return row_data[key];
});
}
var newtable = JSON.parse(row_data[row_data.length-1])
var columns = Object.keys(newtable[0])
//table_id++
row.child(format(table_id, columns)).show()
format_datatable(table_id, newtable, columns)
console.log(table_id)
td.html('▼')
}
})
")
)
})
observe({
# print(input$table_rows_selected)
# print(input$newtable_rows_selected)
print(input$childrow_rows_selected)
})
}
shinyApp(ui = ui, server = server)
I have read most of the posts concerning an error of this type but neither applies to my case. I am new in R, working on an assignment for school based on Nolan and Lang's book Data Science Case Studies in R. I am working on using stats to identify spam, url for the code can be found here, which require working with files from http://spamassassin.apache.org/old/publiccorpus/ (which are pretty big)
Now the problem I am facing is the following (just posting the chunks of code where I have encountered the issue):
sampleSplit = lapply(sampleEmail, splitMessage)
processHeader = function(header)
{
# modify the first line to create a key:value pair
header[1] = sub("^From", "Top-From:", header[1])
headerMat = read.dcf(textConnection(header), all = TRUE)
headerVec = unlist(headerMat)
dupKeys = sapply(headerMat, function(x) length(unlist(x)))
names(headerVec) = rep(colnames(headerMat), dupKeys)
return(headerVec)
}
headerList = lapply(sampleSplit,
function(msg) {
processHeader(msg$header)} )
contentTypes = sapply(headerList, function(header)
header["Content-Type"])
names(contentTypes) = NULL
contentTypes
hasAttach = grep("^ *multi", tolower(contentTypes))
hasAttach
boundaries = getBoundary(contentTypes[ hasAttach ])
boundaries
boundary = boundaries[9]
body = sampleSplit[[15]]$body
bString = paste("--", boundary, sep = "")
bStringLocs = which(bString == body)
bStringLocs
eString = paste("--", boundary, "--", sep = "")
eStringLoc = which(eString == body)
eStringLoc
diff(c(bStringLocs[-1], eStringLoc))
### This code has mistakes in it - and we fix them later!
processAttach = function(body, contentType){
boundary = getBoundary(contentType)
bString = paste("--", boundary, "$", sep = "")
bStringLocs = grep(bString, body)
eString = paste("--", boundary, "--$", sep = "")
eStringLoc = grep(eString, body)
n = length(body)
if (length(eStringLoc) == 0) eStringLoc = n + 1
if (length(bStringLocs) == 1) attachLocs = NULL
else attachLocs = c(bStringLocs[-1], eStringLoc)
msg = body[ (bStringLocs[1] + 1) : min(n, (bStringLocs[2] - 1),
na.rm = TRUE)]
if ( eStringLoc < n )
msg = c(msg, body[ (eStringLoc + 1) : n ])
if ( !is.null(attachLocs) ) {
attachLens = diff(attachLocs, lag = 1)
attachTypes = mapply(function(begL, endL) {
contentTypeLoc = grep("[Cc]ontent-[Tt]ype", body[ (begL + 1) : (endL - 1)])
contentType = body[ begL + contentTypeLoc]
contentType = gsub('"', "", contentType )
MIMEType = sub(" *Content-Type: *([^;]*);?.*", "\\1", contentType)
return(MIMEType)
}, attachLocs[-length(attachLocs)], attachLocs[-1])
}
if (is.null(attachLocs)) return(list(body = msg, attachInfo = NULL) )
else return(list(body = msg,
attachDF = data.frame(aLen = attachLens,
aType = attachTypes,
stringsAsFactors = FALSE)))
}
bodyList = lapply(sampleSplit, function(msg) msg$body)
attList = mapply(processAttach, bodyList[hasAttach],
contentTypes[hasAttach],
SIMPLIFY = FALSE)
lens = sapply(attList, function(processedA)
processedA$attachDF$aLen)
head(lens)
attList[[2]]$attachDF
body = bodyList[hasAttach][[2]]
length(body)
body[35:45]
processAttach = function(body, contentType){
n = length(body)
boundary = getBoundary(contentType)
bString = paste("--", boundary, sep = "")
bStringLocs = which(bString == body)
eString = paste("--", boundary, "--", sep = "")
eStringLoc = which(eString == body)
if (length(eStringLoc) == 0) eStringLoc = n
if (length(bStringLocs) <= 1) {
attachLocs = NULL
msgLastLine = n
if (length(bStringLocs) == 0) bStringLocs = 0
} else {
attachLocs = c(bStringLocs[ -1 ], eStringLoc)
msgLastLine = bStringLocs[2] - 1
}
msg = body[ (bStringLocs[1] + 1) : msgLastLine]
if ( eStringLoc < n )
msg = c(msg, body[ (eStringLoc + 1) : n ])
if ( !is.null(attachLocs) ) {
attachLens = diff(attachLocs, lag = 1)
attachTypes = mapply(function(begL, endL) {
CTloc = grep("^[Cc]ontent-[Tt]ype", body[ (begL + 1) : (endL - 1)])
if ( length(CTloc) == 0 ) {
MIMEType = NA
} else {
CTval = body[ begL + CTloc[1] ]
CTval = gsub('"', "", CTval )
MIMEType = sub(" *[Cc]ontent-[Tt]ype: *([^;]*);?.*", "\\1", CTval)
}
return(MIMEType)
}, attachLocs[-length(attachLocs)], attachLocs[-1])
}
if (is.null(attachLocs)) return(list(body = msg, attachDF = NULL) )
return(list(body = msg,
attachDF = data.frame(aLen = attachLens,
aType = unlist(attachTypes),
stringsAsFactors = FALSE)))
}
readEmail = function(dirName) {
# retrieve the names of files in directory
fileNames = list.files(dirName, full.names = TRUE)
# drop files that are not email
notEmail = grep("cmds$", fileNames)
if ( length(notEmail) > 0) fileNames = fileNames[ - notEmail ]
# read all files in the directory
lapply(fileNames, readLines, encoding = "latin1")
}
processAllEmail = function(dirName, isSpam = FALSE)
{
# read all files in the directory
messages = readEmail(dirName)
fileNames = names(messages)
n = length(messages)
# split header from body
eSplit = lapply(messages, splitMessage)
rm(messages)
# process header as named character vector
headerList = lapply(eSplit, function(msg)
processHeader(msg$header))
# extract content-type key
contentTypes = sapply(headerList, function(header)
header["Content-Type"])
# extract the body
bodyList = lapply(eSplit, function(msg) msg$body)
rm(eSplit)
# which email have attachments
hasAttach = grep("^ *multi", tolower(contentTypes))
# get summary stats for attachments and the shorter body
attList = mapply(processAttach, bodyList[hasAttach],
contentTypes[hasAttach], SIMPLIFY = FALSE)
bodyList[hasAttach] = lapply(attList, function(attEl)
attEl$body)
attachInfo = vector("list", length = n )
attachInfo[ hasAttach ] = lapply(attList,
function(attEl) attEl$attachDF)
# prepare return structure
emailList = mapply(function(header, body, attach, isSpam) {
list(isSpam = isSpam, header = header,
body = body, attach = attach)
},
headerList, bodyList, attachInfo,
rep(isSpam, n), SIMPLIFY = FALSE )
names(emailList) = fileNames
invisible(emailList)
}
Everything runs fine right up to:
emailStruct = mapply(processAllEmail, fullDirNames,
isSpam = rep( c(FALSE, TRUE), 3:2))
emailStruct = unlist(emailStruct, recursive = FALSE)
sampleStruct = emailStruct[ indx ]
save(emailStruct, file="emailXX.rda")
I get the error Error in textConnection(header) : all connections are in use, therefore it doesn't recognize "emailStruct", which I need later on. I seriously don't know how to overcome it so that I can continue with the rest of the code, which requires some of these variables to work.
When you run textConnection() you are opening a text connection, but you are never closing it. Try closing it explicitly after you read from it
read.dcf(tc<-textConnection(header), all = TRUE)
close(tc)