Skip to content

Commit a8fa4ef

Browse files
authored
Merge pull request #13 from ralmond/mongo-split
Mongo split
2 parents c664356 + 71973dc commit a8fa4ef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+5113
-1882
lines changed

.gitignore

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
*~
2-
3-
makeDBuri.Rd~
2+
makeDBuri.Rd~
3+
.Rproj.user
4+
.Rhistory
5+
.DS_Store
6+
\#*

ChangeLog

+54
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,57 @@
1+
2023-07-06 Russell Almond <ralmond@cherry>
2+
3+
* R/MessageQueue.R ($count): Added $count method to MessageQueue,
4+
ListQueue and MongoQueue.
5+
6+
2023-06-23 Russell Almond <ralmond@cherry>
7+
8+
* R/MessageQueue.R (MessageQueue,MongoQueue,ListQueue): New class
9+
for a collection of messages. `MongoQueue` is stored in a
10+
database, and `ListQueue` is just a list of queues.
11+
(fetchNextMessage): New generic function for fetching the next
12+
method from a queue.
13+
(markAsProcessed,markAsError): Added methods for message queue.
14+
(importMessages): Imports messages into the database.
15+
(cleanMessageQueue): Removes unneeded messages from the database.
16+
(resetProcessedMessages): Resets `processed` field of selected
17+
messages.
18+
(purgeMessageQueue_): Removes unneded messages form the database.
19+
20+
* R/InjectionListeners.R (listenerDataTable): -- Method which
21+
extracts the
22+
contents of a message data table as a data frame.
23+
* R/Listeners.R (registerOutput): Logs an output file as available
24+
in the outputdatabase.
25+
(buildListenerSet): wrapper for building whole
26+
listener set from JSON description.
27+
28+
* DESCRIPTION (Version): Fairly major refactoring, moving a number
29+
of things from EABN to here.
30+
31+
32+
2023-05-25 Russell Almond <ralmond@cherry>
33+
34+
* R/Listeners.R: ($notifyListeners): Moved responsibility for setting `sender` field of message to here.
35+
36+
2023-05-24 Russell Almond <ralmond@cherry>
37+
38+
* R/InjectionListener.R (InjectionListener): Changed to pass DB to the class.
39+
40+
* R/UpdateListener.R (UpdateListener): Changed to pass DB to the class.
41+
42+
* R/UpsertListener.R (UpsertListener): Changed to pass DB to the class.
43+
44+
45+
* R/Listeners.R: Moved construction of DB link out of constructors.
46+
(buildListener): dburi and ssl_options are set by the caller, not the config.json This function now builds the Mongo object for the listener.
47+
48+
49+
2023-05-20 Russell Almond <ralmond@cherry>
50+
51+
* R/Listeners.R: Moved various listener types into their own files.
52+
53+
* DESCRIPTION (Imports): Moved the database and json commands to the `mongo` package.
54+
155
2022-04-11 Russell Almond <[email protected]>
256

357
* R/Message.R (parseSimpleData): forced the _id column to always have the name "oid" (necessary for monog).

DESCRIPTION

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: Proc4
2-
Version: 0.6-1
3-
Date: 2022/04/25
2+
Version: 0.8-5
3+
Date: 2023/06/23
44
Title: Four Process Assessment Database and Dispatcher
55
Authors@R:
66
person(given = "Russell",
@@ -11,10 +11,22 @@ Authors@R:
1111
Author: Russell Almond
1212
Maintainer: Russell Almond <[email protected]>
1313
Depends: R (>= 3.0), methods,
14-
Imports: jsonlite, futile.logger
15-
Suggests: mongolite, knitr, rmarkdown, tidyr, CPTtools
14+
Imports: futile.logger, mongo, jsonlite
15+
Suggests: utils, mongolite, knitr, rmarkdown, tidyr, CPTtools, rlang, bookdown, devtools, withr, testthat (>= 3.0.0), Peanut
1616
Description: Utilities for working with messages in the four four process architecture as json objects.
17+
Collate: ErrorHandling.R Message.R MessageQueue.R Listeners.R CaptureListener.R InjectionListener.R TableListener.R UpdateListener.R UpsertListener.R
1718
License: Artistic-2.0
1819
URL: https://pluto.coe.fsu.edu/Proc4
1920
VignetteBuilder: knitr
20-
21+
Support: c(
22+
'Bill & Melinda Gates Foundation grant
23+
"Games as Learning/Assessment: Stealth Assessment" (#0PP1035331,
24+
Val Shute, PI)',
25+
'National Science Foundation grant "DIP:
26+
Game-based Assessment and Support of STEM-related Competencies"
27+
(#1628937, Val Shute, PI)',
28+
'National Science Foundation grant "Mathematical Learning via
29+
Architectual Design and Modeling Using E-Rebuild." (\#1720533,
30+
Fengfeng Ke, PI)',
31+
'Institute of Educational Statistics Grant: "Exploring adaptive cognitive and affective learning support for next-generation STEM learning games." (#R305A170376-20, Val Shute and Russell Almond, PIs')
32+
Config/testthat/edition: 3

NAMESPACE

+29-18
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,50 @@
11
import(methods)
22
import(futile.logger)
3-
importFrom("jsonlite", "toJSON", "fromJSON", "unbox","base64_enc","base64_dec",
3+
import(mongo)
4+
importFrom("jsonlite", "base64_enc","base64_dec",
45
"serializeJSON", "unserializeJSON")
56
importFrom("utils", "limitedLabels")
67

78

89
export(P4Message)
9-
exportClasses(P4Message)
10-
export(m_id,"m_id<-",app,uid,
10+
exportClasses(P4Message,MessageQueue,MongoQueue,ListQueue)
11+
export(app,"app<-",uid,"uid<-",
1112
context,"context<-",
1213
timestamp,"timestamp<-",
13-
sender,mess,details,all.equal.P4Message,
14+
sender,"sender<-",mess,"mess<-",details,
15+
all.equal.P4Message,
1416
processed, markAsProcessed, processingError, markAsError,
15-
makeDBuri)
17+
serializeData,fetchNextMessage,
18+
resetProcessedMessages,cleanMessageQueue,
19+
importMessages)
1620
S3method(all.equal,P4Message,all.equal.P4Message)
17-
exportMethods(m_id,"m_id<-",app,uid,context,"context<-",
18-
timestamp,"timestamp<-",sender,mess,details,
19-
toString,show, processed, processingError)
21+
exportMethods(app,"app<-",uid,"uid<-",context,"context<-",
22+
timestamp,"timestamp<-",sender,"sender<-",
23+
mess,"mess<-",details,"details<-",
24+
toString,show, processed, processingError,
25+
markAsProcessed, markAsError,
26+
resetProcessedMessages,cleanMessageQueue,
27+
importMessages)
2028

21-
export(as.json,as.jlist,saveRec,buildJQuery,buildJQterm,
22-
parseMessage,parseData,unparseData,parseSimpleData,
23-
unboxer,ununboxer, cleanMessageJlist,
24-
getOneRec,getManyRecs)
25-
exportMethods(as.json,as.jlist)
29+
30+
export(buildMessage, cleanMessageJlist)
31+
exportMethods("as.jlist","parse.jlist")
2632

2733
export(receiveMessage,isListener,ListenerSet,notifyListeners,
34+
listeningFor, clearMessages,
2835
InjectionListener,UpdateListener,UpsertListener,CaptureListener,
29-
TableListener, buildListener,listenerName, resetListeners)
30-
exportClasses(ListenerSet,NullListenerSet,MongoDB,CaptureListener,
31-
InjectionListener,
36+
TableListener, buildListener, listenerName, resetListeners,
37+
buildListenerSet, registerOutput, listenerDataTable,
38+
updateTable,generateListenerExports)
39+
exportClasses(ListenerSet,NullListenerSet,RefListener,
40+
CaptureListener,InjectionListener,
3241
UpdateListener,UpsertListener,TableListener)
3342
exportMethods(isListener,receiveMessage,notifyListeners,listenerName,
34-
resetListeners)
43+
resetListeners,clearMessages,listeningFor, registerOutput,
44+
listenerDataTable)
3545

36-
export(withFlogging)
46+
exportClasses(mongoAppender)
47+
export(withFlogging,mongoAppender)
3748

3849

3950

Proc4.Rproj

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Version: 1.0
2+
3+
RestoreWorkspace: Default
4+
SaveWorkspace: Default
5+
AlwaysSaveHistory: Default
6+
7+
EnableCodeIndexing: Yes
8+
UseSpacesForTab: Yes
9+
NumSpacesForTab: 2
10+
Encoding: UTF-8
11+
12+
RnwWeave: Sweave
13+
LaTeX: pdfLaTeX
14+
15+
AutoAppendNewline: Yes
16+
StripTrailingWhitespace: Yes
17+
18+
BuildType: Package
19+
PackageUseDevtools: Yes
20+
PackageInstallArgs: --no-multiarch --with-keep.source

R/CaptureListener.R

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
#################################################
3+
## CaptureListener
4+
5+
## This a simple listener whose goal is to simply hold the message to
6+
## it can be checked later.
7+
8+
CaptureListener <-
9+
setRefClass("CaptureListener",
10+
fields=c(messages="list"),
11+
methods=list(
12+
initialize = function(name="Capture",messages=list(),...)
13+
callSuper(name=name,messages=messages,...),
14+
receiveMessage = function (message) {
15+
messages <<- c(message,messages)
16+
},
17+
lastMessage = function() {
18+
if(length(messages)==0L) return(NULL)
19+
messages[[1]]
20+
},
21+
reset = function(app) {
22+
messages <<- list()
23+
}),
24+
contains="RefListener")
25+
26+
CaptureListener <- function (name="Capture",messages=list(),messSet=character(),...) {
27+
new("CaptureListener",name=name,messages=messages,messSet=messSet)
28+
}
29+
30+
setMethod("listenerDataTable","TableListener",
31+
function(listener, appid=character())
32+
as.data.frame(sapply(messages,attributes))
33+
)
34+
35+
36+

R/ErrorHandling.R

+51-2
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,47 @@ withFlogging <- function(expr,...,context=deparse(substitute(expr)),
122122
invisible(structure(msg, class = "try-error", condition = obj)))
123123
}
124124

125+
## Breaks futile.logger line into its parts.
126+
parseline <- function(line) {
127+
bracket1 <- regexpr("[",line,fixed=TRUE)
128+
bracket2 <- regexpr("]",line,fixed=TRUE)
129+
list(level=trimws(substr(line,1,bracket1-1)),
130+
time=strptime(substr(line,bracket1+1,bracket2-1),format="%Y-%m-%d %H:%M:%S"),
131+
message=trimws(substring(line,bracket2+1)))
132+
}
133+
134+
## Forces Date into Mongo Format.
135+
mongoDate <- function (dtime) {
136+
jsonlite::fromJSON(jsonlite::toJSON(mongo::unboxer(dtime),POSIXt="mongo"),FALSE)
137+
}
138+
139+
mongoAppender <-
140+
setRefClass("mongoAppender",
141+
fields=c(db="JSONDB",
142+
app="character",
143+
engine="character",
144+
tee="character"),
145+
methods=c(
146+
initialize = function(app="default",
147+
engine="Unspecified",
148+
db=mongo::MongoDB("Log",noMongo=TRUE),
149+
tee=character()) {
150+
callSuper(db=db,app=app,engine=engine,tee=tee)
151+
},
152+
logit = function(line) {
153+
pline <- parseline(line)
154+
entry <- buildJQuery(app=app,engine=engine,level=pline$level,
155+
timestamp=pline$time,
156+
message=pline$message)
157+
mdbInsert(db,entry)
158+
if (length(tee) > 0L) {
159+
cat(line,file=tee,append=TRUE,sep="")
160+
}
161+
},
162+
logger = function() {
163+
function(line) {.self$logit(line)}
164+
}))
165+
125166

126167
shinyAppender <-
127168
setRefClass("shinyAppender",
@@ -136,14 +177,22 @@ shinyAppender <-
136177
callSuper(file=file, field=field, steps=steps,
137178
messages=messages)
138179
},
139-
update = function (line)
180+
update = function (line, output, renderer=function(tab) shiny::renderTable(tab,colname=FALSE))
140181
{
141182
if (length(file) == 1L && nchar(file)>0L) {
142183
cat(line, file = file, append = TRUE, sep = "")
143184
}
144185
messages$Messages <<- c(messages$Messages,line)
145186
if (length(field)==1L && nchar(field)>0L) {
146-
output[[field]] <- renderTable(messages,colnames=FALSE)
187+
# Call the render for a test case.
188+
if (is.null(output)) do.call(renderer,list(messages))
189+
} else {
190+
output[[field]] <- do.call(renderer,list(messages))
191+
}
192+
},
193+
logger = function() {
194+
function (line) {
195+
.self$update(line)
147196
}
148197
}
149198
))

R/InjectionListener.R

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#################################################
2+
## InjectionListener
3+
4+
## This a simple listener whose goal is to simply to inject the
5+
## message into a mongo collection where it can be used as a queue.
6+
7+
InjectionListener <-
8+
setRefClass("InjectionListener",
9+
list(),
10+
methods=list(
11+
initialize=
12+
function(name="Injection",
13+
db=mongo::MongoDB(noMongo=TRUE),
14+
messSet=character(),
15+
...) {
16+
callSuper(name=name,db=db,
17+
messSet=messSet,
18+
...)
19+
},
20+
messdb = function() {db},
21+
receiveMessage = function (message) {
22+
flog.debug("Sending message %s",toString(message))
23+
flog.debug(".. from %s",sender(message))
24+
flog.trace("Message:",x=as.jlist(message,
25+
attributes(message)),
26+
capture=TRUE)
27+
mongo::m_id(message) <- NA_character_
28+
mdbInsert(messdb(),as.json(message,serialize=TRUE))
29+
},
30+
reset = function(app) {
31+
mdbRemove(messdb(),buildJQuery(app=app))
32+
}
33+
),
34+
contains="RefListener")
35+
36+
InjectionListener <- function (name="Injection",
37+
db=mongo::MongoDB(noMongo=TRUE),
38+
messSet=character(),
39+
...) {
40+
new("InjectionListener",name=name,db=db,messSet=messSet,...)
41+
}
42+
43+
44+
setMethod("listenerDataTable","InjectionListener",
45+
function(listener,appid=character()) {
46+
stat1 <- mdbFind(listener$messdb(),buildJQuery(app=appid))
47+
if (isTRUE(nrow(stat1) > 0L)) {
48+
stat1data <- t(sapply(stat1$data,parseData))
49+
sdat <- data.frame(stat1[,c("app","uid","context","timestamp")],
50+
stat1data)
51+
sdat$app <- basename(sdat$app)
52+
return(sdat)
53+
} else {
54+
flog.warn("No records in statistics file.")
55+
return(NULL)
56+
}
57+
})
58+
59+
## These are left here as they may be useful later; however, they are
60+
buildHistMat <- function (col, app, uid) {
61+
stat1 <- mdbFind(col,buildJQuery(app=app, uid=uid))
62+
stat1d <- lapply(stat1$data,function (d) Peanut::flattenStats(parseData(d)))
63+
data.frame(stat1[,c("app","uid","context","timestamp")],
64+
do.call(rbind,stat1d))
65+
}
66+
67+
buildAppHist <- function (col, app) {
68+
uids <- col$distinct("uid",buildJQuery(app=app))
69+
do.call(rbind,
70+
lapply(uids,function(u) buildHistMat(col, app, u)))
71+
}

0 commit comments

Comments
 (0)