blob: 56f8d488b6ef6014e4bfa25263bda69024705623 [file] [log] [blame]
--[[
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
pminfo.lua Pony Mail Information module
]]--
local JSON = require 'cjson'
local elastic = require 'lib/elastic'
local cross = require 'lib/cross'
local config = require 'lib/config'
function handle(r)
cross.contentType(r, "application/json")
local DEBUG = config.debug or false
-- Cannot define local variables within a conditional block
local t = {}
local START = DEBUG and r:clock() or nil
local tnow = START
local DD = 14
local NOWISH = math.floor(os.time() / 1800)
local PMINFO_CACHE_KEY = "pminfo_cache_" .. r.hostname .. "-" .. NOWISH
local cache = r:ivm_get(PMINFO_CACHE_KEY)
if cache then
r:puts(cache)
return cross.OK
end
-- Debug time point 1
if DEBUG then
table.insert(t, r:clock() - tnow)
tnow = r:clock()
end
local daterange = {gt = "now-"..DD.."d", lt = "now+1d" }
-- common query
local QUERY = {
bool = {
must = {
{
range = {
date = daterange
}
},
{
term = {
private = false
}
}
}
}
}
--[[ Get active lists ]]--
local doc = elastic.raw {
size = 0, -- we don't need the hits themselves
query = QUERY,
aggs = {
nlists = { -- total active lists
cardinality = {
field = "list_raw"
}
},
cards = { -- total participants
cardinality = {
field = "from_raw"
}
},
weekly = { -- histogram of emails
date_histogram = {
field = "date",
interval = "1d"
}
}
}
}
local nal = doc.aggregations.nlists.value -- number of active lists
local total_docs = doc.hits.total
local no_senders = doc.aggregations.cards.value
-- Debug time point 2
if DEBUG then
table.insert(t, r:clock() - tnow)
tnow = r:clock()
end
local activity = {}
for k, v in pairs (doc.aggregations.weekly.buckets) do
table.insert(activity, {v.key, v.doc_count})
end
-- Debug time point 3
if DEBUG then
table.insert(t, r:clock() - tnow)
tnow = r:clock()
end
-- Get threads
local num_threads = 0
local emails = {}
local squery = {
_source = {'message-id','in-reply-to','subject','references','epoch'},
query = QUERY,
sort = {
{
epoch = {
order = "desc"
}
}
},
size = elastic.MAX_RESULT_WINDOW
}
local hits = {}
-- check if we need to use scrolling
if total_docs > elastic.MAX_RESULT_WINDOW then
local sid
doc, sid = elastic.scroll(squery) -- init the scroll
while doc and doc.hits and doc.hits.hits and #doc.hits.hits > 0 do -- scroll as long as we get new results
for k, v in pairs(doc.hits.hits) do
table.insert(hits, v)
end
doc, sid = elastic.scroll(sid)
end
elastic.clear_scroll(sid) -- we're done with the sid, release it
else
local doc = elastic.raw(squery)
hits = doc.hits.hits
end
-- Debug time point 4
if DEBUG then
table.insert(t, r:clock() - tnow)
tnow = r:clock()
end
for k = #hits, 1, -1 do
local v = hits[k]
local email = v._source
local mid = email['message-id']
local irt = email['in-reply-to']
email.id = v._id
email.irt = irt
emails[mid] = {
tid = v._id,
nest = 1,
children = {
}
}
if not irt or #irt == 0 then
irt = email.subject:gsub("^[a-zA-Z]+:%s+", "")
end
if not emails[irt] then
for ref in email.references:gmatch("(%S+)") do
if emails[ref] then
irt = ref
break
end
end
end
-- If we can't match by in-reply-to or references, match/group by subject, ignoring Re:/Fwd:/etc
if not emails[irt] then
irt = email.subject:gsub("^[a-zA-Z]+:%s+", "")
while irt:match("^[a-zA-Z]+:%s+") do
irt = irt:gsub("^[a-zA-Z]+:%s+", "")
end
end
if emails[irt] then
if emails[irt].nest < 50 then
emails[mid].nest = emails[irt].nest + 1
table.insert(emails[irt].children, emails[mid])
end
else
if (#email['in-reply-to'] > 0) then
emails[irt] = {
children = {
emails[mid]
},
nest = 1,
tid = v._id
}
emails[mid].nest = emails[irt].nest + 1
end
num_threads = num_threads + 1
end
end
-- Debug time point 5
if DEBUG then
table.insert(t, r:clock() - tnow)
tnow = r:clock()
end
JSON.encode_max_depth(500)
local listdata = {}
listdata.max = MAXRESULTS
listdata.no_threads = num_threads
listdata.hits = total_docs
listdata.participants = no_senders
listdata.no_active_lists = nal
if DEBUG then
listdata.took = r:clock() - START
listdata.debug = t
end
listdata.activity = activity
local output = JSON.encode(listdata)
r:ivm_set(PMINFO_CACHE_KEY, output)
r:puts(output)
return cross.OK
end
cross.start(handle)