Skip to content

Commit

Permalink
Implement notes API, other changes
Browse files Browse the repository at this point in the history
  • Loading branch information
ajkeller34 committed Oct 18, 2016
1 parent fcf0c1c commit d13eaca
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 33 deletions.
53 changes: 48 additions & 5 deletions src/ICDataServer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ODBC
export newinstrument, updateinstrument, deleteinstrument, listinstruments
export newuser, updateuser, deleteuser, listusers
export newserver, updateserver, deleteserver, listservers
export newnote, listnotes
export newjob, updatejob

const ctx = ZMQ.Context()
Expand All @@ -20,16 +21,58 @@ if isfile(confpath)
if reduce(&, k in keys(confd) for k in
("jobsock", "dsn", "username", "password"))

ZMQ.bind(jobsock, d["jobsock"])
const dsn = ODBC.DSN(d["db"], d["username"], d["password"])
ZMQ.bind(jobsock, confd["jobsock"])
const dsn = ODBC.DSN(confd["dsn"], confd["username"], confd["password"])
else
error("set `dbserver` key in $(confpath) to have a valid ",
"ZeroMQ connection string.")
if !"jobsock" in keys(confd)
error("set `jobsock` key in $(confpath) to have a valid ",
"ZeroMQ connection string.")
elseif !"dsn" in keys(confd)
error("set `dsn` key in $(confpath) to have a valid DSN identifier.")
elseif !"username" in keys(confd)
error("set `username` key in $(confpath) to a valid username.")
else
error("set `password` key in $(confpath) for the given user.")
end
end
else
error("config file not found at $(confpath).")
end

function serve(;debug=false)
while true
msg = ZMQ.recv(jobsock)
out = convert(IOStream, msg)
seekstart(out)
d = deserialize(out)
debug && println(STDOUT, d)
handle(d)
end
end

function handle(jr::NewJobRequest)
job_id, jobsubmit = newjob(dsn; jr.args...)
io = IOBuffer()
serialize(io, (job_id, jobsubmit))
ZMQ.send(jobsock, ZMQ.Message(io))
end

function handle(jr::UpdateJobRequest)
response = try
updatejob(dsn, jr.job_id; jr.args...)
true
catch y
false # catch errors so that we can issue a proper reply regardless
end

# reply with `true` or `false` for success
# (a reply must be sent to avoid zmq errors with a REQ/REP pattern)
# maybe in the future we'd actually pass the error back
io = IOBuffer()
serialize(io, response)
ZMQ.send(jobsock, ZMQ.Message(io))
end

function listtables(dsn)
ODBC.query(dsn,
"""
Expand All @@ -56,7 +99,7 @@ include("users.jl")
include("servers.jl")
include("instruments.jl")
include("jobs.jl")
include("notes.jl")
include("setup.jl")


end
20 changes: 15 additions & 5 deletions src/jobs.jl
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
# would be good to submit a PR to ODBC.jl...
import Base.convert
convert(::Type{DateTime}, dt::ODBC.API.SQLTimestamp) =
DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
round(dt.fraction/1e6))

"""
```
newjob(dsn; cryostat="", uname="", device="",
newjob(dsn; cryostat="", username="", device="",
nmeas=1, jobstart="", jobstop="", jobstatus=0, dataserver="")
```
Create a new job in the `jobs` table. This function will return a `DataFrame`
containing the columns `job_id` and `jobsubmit` with the inserted job id
and job submission time.
"""
function newjob(dsn; cryostat="", uname="", device="",
function newjob(dsn; cryostat="", username="", device="",
nmeas=1, jobstart="", jobstop="", jobstatus=0, dataserver="")

dformat(x) = x == "" ? :NULL : "'$x'"
ODBC.query(dsn, """
INSERT INTO jobs (cryostat, uname, device, nmeas, jobsubmit, jobstart,
jobstop, jobstatus, dataserver) VALUES ('$cryostat', '$uname',
df = ODBC.query(dsn, """
INSERT INTO jobs (cryostat, username, device, nmeas, jobsubmit, jobstart,
jobstop, jobstatus, dataserver) VALUES ('$cryostat', '$username',
'$device', $nmeas, $(dformat(now())), $(dformat(jobstart)),
$(dformat(jobstop)), $jobstatus, '$dataserver')
RETURNING job_id, jobsubmit;
""")
get(df[:job_id][1]), convert(DateTime, get(df[:jobsubmit][1]))
end

"""
Expand All @@ -37,3 +44,6 @@ function updatejob(dsn, job_id; kwargs...)
ODBC.execute!(dsn, "UPDATE jobs SET "*pstr*" WHERE job_id = $job_id;")
end
end

precompile(newjob, (ODBC.DSN, ))
precompile(updatejob, (ODBC.DSN, Integer))
42 changes: 42 additions & 0 deletions src/notes.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""
```
newnote(dsn, job_id, username, note)
```
Create a new note in the `notes` table. A note is associated with a particular
job and therefore a `job_id` is required, along with a `username` to indicate
who wrote the note.
"""
function newnote(dsn, job_id, username, note)
ODBC.execute!(dsn, """
INSERT INTO notes (job_id, username, dt, note) VALUES
($job_id, '$username', '$(now())', '$note');
""")
end

"""
```
listnotes(dsn; job_id=0, username="")
```
If keyword arguments `job_id` and/or `username` are provided, list the notes
in the `notes` table that are at the intersection of those conditions.
Otherwise, list all notes in the `notes` table.
"""
function listnotes(dsn; job_id=0, username="")
if job_id != 0 && username != ""
wherestr = "WHERE "
if job_id != 0
wherestr *= "job_id = $job_id"
end
if username != ""
wherestr *= ", username = '$username'"
end
else
wherestr = ""
end

ODBC.query(dsn, """
SELECT job_id, username, dt, note FROM notes $wherestr;
""")
end
26 changes: 13 additions & 13 deletions src/setup.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function jobstable(dsn)
CREATE TABLE IF NOT EXISTS jobs(
job_id bigserial PRIMARY KEY,
cryostat character varying,
uname character varying REFERENCES users(uname),
username character varying REFERENCES users(username),
device character varying,
nmeas integer,
jobsubmit timestamp NOT NULL,
Expand Down Expand Up @@ -107,13 +107,13 @@ function userstable(dsn, path; filename="")
ODBC.execute!(dsn,
"""
CREATE TABLE IF NOT EXISTS users(
uname character varying PRIMARY KEY,
first character varying,
middle character varying,
last character varying,
email character varying,
phone character varying,
office character varying
username character varying PRIMARY KEY,
first character varying,
middle character varying,
last character varying,
email character varying,
phone character varying,
office character varying
);
""")

Expand All @@ -125,7 +125,7 @@ function userstable(dsn, path; filename="")
vstr = reduce((a,b)->a*","*b, "'$(d[ki])'" for ki in k)
pstr = reduce((a,b)->a*","*b, "$ki = '$(d[ki])'" for ki in k)
query = """
INSERT INTO users ($kstr) VALUES ($vstr) ON CONFLICT (uname)
INSERT INTO users ($kstr) VALUES ($vstr) ON CONFLICT (username)
DO UPDATE SET $pstr
"""
ODBC.execute!(dsn, query)
Expand All @@ -138,7 +138,7 @@ function servertable(dsn, path; filename="")
"""
CREATE TABLE IF NOT EXISTS servers(
alias character varying PRIMARY KEY,
addr character varying NOT NULL,
address character varying NOT NULL,
port integer
);
""")
Expand All @@ -151,7 +151,7 @@ function servertable(dsn, path; filename="")
vstr = reduce((a,b)->a*","*b, "'$(d[ki])'" for ki in k)
pstr = reduce((a,b)->a*","*b, "$ki = '$(d[ki])'" for ki in k)
query = """
INSERT INTO users ($kstr) VALUES ($vstr) ON CONFLICT (name)
INSERT INTO servers ($kstr) VALUES ($vstr) ON CONFLICT (alias)
DO UPDATE SET $pstr
"""
ODBC.execute!(dsn, query)
Expand All @@ -165,9 +165,9 @@ function notestable(dsn)
CREATE TABLE IF NOT EXISTS notes(
note_id serial PRIMARY KEY,
job_id bigint REFERENCES jobs(job_id) NOT NULL,
uname character varying REFERENCES users(uname) NOT NULL,
username character varying REFERENCES users(username) NOT NULL,
dt timestamp NOT NULL,
notes character varying NOT NULL
note character varying NOT NULL
);
""")
end
20 changes: 10 additions & 10 deletions src/users.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""
```
newuser(dsn, uname, name; email="", phone="", office="")
newuser(dsn, username, name; email="", phone="", office="")
```
This function creates a new user in the `users` table of the database.
E-mail, phone, office are useful for contacting users about their measurements.
"""
function newuser(dsn, uname, name; email="", phone="", office="")
function newuser(dsn, username, name; email="", phone="", office="")
first, middle, last = "", "", ""
names = split(name, " ")
if length(names) == 1
Expand All @@ -22,37 +22,37 @@ function newuser(dsn, uname, name; email="", phone="", office="")
ph = replace(phone, r"[-\+\(\)\s]", "")
ODBC.execute!(dsn, """
INSERT INTO users VALUES (
'$uname', '$first', '$middle', '$last', '$email', '$phone', '$office'
'$username', '$first', '$middle', '$last', '$email', '$phone', '$office'
);
""")
end

"""
```
updateuser(dsn, uname; kwargs...)
updateuser(dsn, username; kwargs...)
```
Update an existing user in the `users` table, identified by `uname`. Specify
Update an existing user in the `users` table, identified by `username`. Specify
the fields to update with keyword arguments specified in
[`ICDataServer.newuser`](@ref).
"""
function updateuser(dsn, uname; kwargs...)
function updateuser(dsn, username; kwargs...)
isempty(kwargs) && return
pstr = reduce((a,b)->a*","*b, "$k = '$v'" for (k,v) in kwargs)
for (k,v) in kwargs
ODBC.execute!(dsn, "UPDATE users SET "*pstr*" WHERE uname = $uname;")
ODBC.execute!(dsn, "UPDATE users SET "*pstr*" WHERE username = $username;")
end
end

"""
```
deleteuser(dsn, uname)
deleteuser(dsn, username)
```
Delete a user from the `users` table by providing the username.
"""
function deleteuser(dsn, uname)
ODBC.execute!(dsn, "DELETE FROM users WHERE uname = '$uname';")
function deleteuser(dsn, username)
ODBC.execute!(dsn, "DELETE FROM users WHERE username = '$username';")
end

"""
Expand Down

0 comments on commit d13eaca

Please sign in to comment.