Update.
This commit is contained in:
parent
5434a4e9bc
commit
bbe1907063
@ -5,6 +5,25 @@ import json
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import time
|
||||||
|
import socket
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
dns_resolved_addr = socket.gethostbyname(hostname)
|
||||||
|
|
||||||
|
|
||||||
|
class TimeIt:
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
self.start = time.time()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
self.end = time.time()
|
||||||
|
self.duration = self.end - self.start
|
||||||
|
print(f"{self.name} took {self.duration} seconds")
|
||||||
|
|
||||||
class BaseView(web.View):
|
class BaseView(web.View):
|
||||||
|
|
||||||
@ -27,14 +46,14 @@ class BaseView(web.View):
|
|||||||
ip=self.ip,
|
ip=self.ip,
|
||||||
key=key,
|
key=key,
|
||||||
value=json.dumps(value,default=str),
|
value=json.dumps(value,default=str),
|
||||||
created=str(datetime.now())
|
created=datetime.now()
|
||||||
)
|
)
|
||||||
record.update(kwargs)
|
record.update(kwargs)
|
||||||
self.db['session'].upsert(dict(
|
self.db['session'].upsert(dict(
|
||||||
key=key,
|
key=key,
|
||||||
value=json.dumps(value,default=str),
|
value=json.dumps(value,default=str),
|
||||||
ip=self.ip,
|
ip=self.ip,
|
||||||
created=str(datetime.now())
|
created=datetime.now()
|
||||||
),['id','key'])
|
),['id','key'])
|
||||||
return record
|
return record
|
||||||
|
|
||||||
@ -46,9 +65,9 @@ class BaseView(web.View):
|
|||||||
|
|
||||||
def insert(self, table, data):
|
def insert(self, table, data):
|
||||||
data['ip'] = self.ip
|
data['ip'] = self.ip
|
||||||
data['created'] = str(datetime.now())
|
data['created'] = datetime.now()
|
||||||
for key,value in data.items():
|
for key,value in data.items():
|
||||||
data[key] = str(value)
|
data[key] = value
|
||||||
#data = json.loads(json.dumps(data,default=str))
|
#data = json.loads(json.dumps(data,default=str))
|
||||||
return self.db[table].insert(data)
|
return self.db[table].insert(data)
|
||||||
|
|
||||||
@ -58,6 +77,24 @@ class BaseView(web.View):
|
|||||||
data['ip'] = self.ip
|
data['ip'] = self.ip
|
||||||
return [dict(d) for d in self.db[table].find(**data)]
|
return [dict(d) for d in self.db[table].find(**data)]
|
||||||
|
|
||||||
|
class Visit:
|
||||||
|
|
||||||
|
def __init__(self, db, visit):
|
||||||
|
self.__dict__.update(dict(visit))
|
||||||
|
self._db = db
|
||||||
|
self.html_bytes = len(self.html)
|
||||||
|
|
||||||
|
self.count = db['event'].count(visit_id=self.visit_id)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return json.dumps(self.__dict__,default=str)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def find(cls, db, where=None):
|
||||||
|
if not where:
|
||||||
|
where = {}
|
||||||
|
return [cls(db, visit) for visit in db['visit'].find(**where)]
|
||||||
|
|
||||||
class EventView(BaseView):
|
class EventView(BaseView):
|
||||||
|
|
||||||
document_fields = ['title','html','domain','href']
|
document_fields = ['title','html','domain','href']
|
||||||
@ -71,38 +108,19 @@ class EventView(BaseView):
|
|||||||
return data.get('event') == 'click'
|
return data.get('event') == 'click'
|
||||||
|
|
||||||
def get_record(self, data):
|
def get_record(self, data):
|
||||||
|
record = {}
|
||||||
try:
|
for field in self.document_fields:
|
||||||
record = {}
|
if field in data:
|
||||||
for field in self.document_fields:
|
record[field] = data[field]
|
||||||
if field in data:
|
for field in self.event_fields:
|
||||||
record[field] = data[field]
|
if field in data:
|
||||||
if len(record.keys()) == len(self.document_fields):
|
record[field] = data[field]
|
||||||
return record
|
for field in self.click_fields:
|
||||||
except KeyError:
|
if field in data:
|
||||||
pass
|
record[field] = data[field]
|
||||||
|
if not record.get('event') and not record.get('html'):
|
||||||
try:
|
return None
|
||||||
record = {}
|
return record
|
||||||
for field in self.event_fields:
|
|
||||||
if field in data:
|
|
||||||
record[field] = data[field]
|
|
||||||
if len(record.keys()) == len(self.event_fields):
|
|
||||||
return record
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
record = {}
|
|
||||||
for field in self.click_fields:
|
|
||||||
if field in data:
|
|
||||||
record[field] = data[field]
|
|
||||||
if len(record.keys()) == len(self.click_fields):
|
|
||||||
return record
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
async def get(self):
|
async def get(self):
|
||||||
ws = web.WebSocketResponse()
|
ws = web.WebSocketResponse()
|
||||||
@ -113,47 +131,47 @@ class EventView(BaseView):
|
|||||||
print(f"Socket connected from {self.ip}.")
|
print(f"Socket connected from {self.ip}.")
|
||||||
|
|
||||||
async for msg in ws:
|
async for msg in ws:
|
||||||
if msg.type == web.WSMsgType.TEXT:
|
with TimeIt("process_event"):
|
||||||
# Echo the received message back to the client
|
if msg.type == web.WSMsgType.TEXT:
|
||||||
try:
|
# Echo the received message back to the client
|
||||||
data = msg.json()
|
try:
|
||||||
except Exception as ex:
|
data = msg.json()
|
||||||
print(ex)
|
except Exception as ex:
|
||||||
continue
|
print(ex)
|
||||||
|
continue
|
||||||
original_data = data
|
|
||||||
data = self.get_record(data)
|
original_data = data
|
||||||
if not data:
|
data = self.get_record(data)
|
||||||
print("Not a valid record:", original_data)
|
if not data:
|
||||||
exit(1)
|
print("Not a valid record:", original_data)
|
||||||
continue
|
exit(1)
|
||||||
|
continue
|
||||||
if self.is_document_record(data):
|
|
||||||
html = data.get('html')
|
if self.is_document_record(data):
|
||||||
record = dict(
|
html = data.get('html')
|
||||||
html=html,
|
record = dict(
|
||||||
visit_id=visit_id,
|
html=html,
|
||||||
domain=data.get('domain'),
|
visit_id=visit_id,
|
||||||
href=data.get('href'),
|
domain=data.get('domain'),
|
||||||
title=data.get('title')
|
href=data.get('href'),
|
||||||
)
|
title=data.get('title')
|
||||||
self.insert('visit', record)
|
)
|
||||||
self.app.view_count += 1
|
self.insert('visit', record)
|
||||||
elif self.is_click_record(data):
|
self.app.view_count += 1
|
||||||
data['visit_id'] = visit_id
|
elif self.is_click_record(data):
|
||||||
record = data
|
data['visit_id'] = visit_id
|
||||||
self.insert('click', record)
|
record = data
|
||||||
else:
|
self.insert('click', record)
|
||||||
data['visit_id'] = visit_id
|
else:
|
||||||
record = data
|
data['visit_id'] = visit_id
|
||||||
self.insert('event', record)
|
record = data
|
||||||
self.app.event_count += 1
|
self.insert('event', record)
|
||||||
|
self.app.event_count += 1
|
||||||
print(record)
|
|
||||||
print(self.app.view_count, self.app.event_count)
|
self.app.sset('view_count',self.app.view_count)
|
||||||
|
self.app.sset('event_count',self.app.event_count)
|
||||||
elif msg.type == web.WSMsgType.ERROR:
|
elif msg.type == web.WSMsgType.ERROR:
|
||||||
print(f'Socket closed: {ws.exception()}')
|
print(f'Socket closed: {ws.exception()}')
|
||||||
|
|
||||||
print("Socket cracefully closed.")
|
print("Socket cracefully closed.")
|
||||||
return ws
|
return ws
|
||||||
@ -168,10 +186,37 @@ class Application(BaseApplication):
|
|||||||
super().__init__(template_path=self.template_path,db_path=db_path, *args, **kwargs)
|
super().__init__(template_path=self.template_path,db_path=db_path, *args, **kwargs)
|
||||||
self.router.add_get("/", self.index_handler)
|
self.router.add_get("/", self.index_handler)
|
||||||
self.router.add_view("/event", EventView)
|
self.router.add_view("/event", EventView)
|
||||||
|
self.router.add_view("/dashboard", self.dashboard_handler)
|
||||||
|
self.router.add_view("/visit", self.visit_handler)
|
||||||
self.router.add_static("/static", pathlib.Path(__file__).parent.joinpath("static"))
|
self.router.add_static("/static", pathlib.Path(__file__).parent.joinpath("static"))
|
||||||
|
|
||||||
|
|
||||||
|
async def visit_handler(self,handler):
|
||||||
|
totals = {}
|
||||||
|
|
||||||
|
with TimeIt("visit_handler"):
|
||||||
|
views = [dict(view) for view in self.db['visit'].find()]
|
||||||
|
date_start = None
|
||||||
|
for view in views:
|
||||||
|
if not date_start:
|
||||||
|
date_start = view['created']
|
||||||
|
view['count'] = self.db['event'].count(visit_id=view['visit_id'])
|
||||||
|
date_end = view['created']
|
||||||
|
|
||||||
|
if not view['href'] in totals:
|
||||||
|
totals[view['href']] = {"title": view['title'], "count": view['count']}
|
||||||
|
totals[view['href']]['count'] += view['count']
|
||||||
|
views = json.loads(json.dumps(views,default=str))
|
||||||
|
return web.json_response(dict(date_start=str(date_start),date_end=str(date_end),view_count=self.sget("view_count"),event_count=self.sget("event_count"),totals=totals))
|
||||||
|
|
||||||
|
def ip(self, request):
|
||||||
|
return request.headers.get("X-Forwarded-For",request.remote)
|
||||||
|
|
||||||
async def index_handler(self, request):
|
async def index_handler(self, request):
|
||||||
|
|
||||||
return await self.render_template("index.html",request, dict(view_count=self.view_count,event_count=self.event_count))
|
return await self.render_template("index.html",request, dict(view_count=self.view_count,event_count=self.event_count,ip=self.ip(request),hostname=hostname,dns_resolved_addr=dns_resolved_addr))
|
||||||
|
|
||||||
|
async def dashboard_handler(self, request):
|
||||||
|
visits = Visit.find(self.db)
|
||||||
|
db_mbs = pathlib.Path(self.db_path.lstrip("sqlite:///")).stat().st_size * 0.000001
|
||||||
|
return await self.render_template("dashboard.html", request, dict(view_count=self.view_count,event_count=self.event_count,ip=self.ip(request),hostname=hostname,dns_resolved_addr=dns_resolved_addr,visits=visits,visits_count=len(visits),db_mbs=db_mbs))
|
||||||
|
@ -79,19 +79,26 @@ class Metriki {
|
|||||||
document.addEventListener('click', (e) => {
|
document.addEventListener('click', (e) => {
|
||||||
me.emit({
|
me.emit({
|
||||||
"event":"click",
|
"event":"click",
|
||||||
"target":e.target.id || e.target.className || e.target.tagName
|
"target":e.target.id || e.target.className || e.target.tagName,
|
||||||
|
"scroll_height":document.documentElement.scrollHeight,
|
||||||
|
"scroll_left":document.documentElement.scrollLeft,
|
||||||
|
"scroll_top":document.documentElement.scrollTop,
|
||||||
|
"client_width":document.documentElement.clientWidth,
|
||||||
|
"client_height":document.documentElement.clientHeight,
|
||||||
|
"page_x":e.pageX,
|
||||||
|
"page_y":e.pageY,
|
||||||
|
"screen_x":e.screenX,
|
||||||
|
"screen_y":e.screenY,
|
||||||
|
"client_x":e.clientX,
|
||||||
|
"client_y":e.clientY,
|
||||||
|
"target_x":e.target.offsetLeft,
|
||||||
|
"target_y":e.target.offsetTop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.url = window.location.href.replace("http://", "ws://").replace("https://", "wss://").replace("www.", "")
|
this.url = window.location.protocol == 'http:' ? 'ws://localhost:4000/event' : 'wss://metriki.molodetz.nl/event'
|
||||||
if(this.url.endsWith("/")){
|
|
||||||
this.url += "event"
|
|
||||||
}else{
|
|
||||||
this.url += "/event"
|
|
||||||
}
|
|
||||||
this.url = "wss://metriki.molodetz.nl/event"
|
|
||||||
const me = this
|
const me = this
|
||||||
this.ws = new WebSocket(this.url)
|
this.ws = new WebSocket(this.url)
|
||||||
this.ws.onopen = () => {
|
this.ws.onopen = () => {
|
||||||
|
@ -7,8 +7,7 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>Metriki</h1>
|
<h1>Metriki</h1>
|
||||||
|
{{ dns_resolved_addr }}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user