diff --git a/server/src/uds/core/util/stats/events.py b/server/src/uds/core/util/stats/events.py index 11a1d9e6..6d1484b9 100644 --- a/server/src/uds/core/util/stats/events.py +++ b/server/src/uds/core/util/stats/events.py @@ -102,8 +102,8 @@ def getEvents(obj, eventType, **kwargs): else: owner_id = obj.pk - for i in statsManager().getEvents(__transDict[type(obj)], eventType, owner_id, since, to): - val = (datetime.datetime.fromtimestamp(i.stamp), i.fld1, i.fld2, i.fld3) + for i in statsManager().getEvents(__transDict[type(obj)], eventType, owner_id=owner_id, since=since, to=to): + val = (datetime.datetime.fromtimestamp(i.stamp), i.fld1, i.fld2, i.fld3, i.fld4, i.event_type) yield val diff --git a/server/src/uds/models/StatsEvents.py b/server/src/uds/models/StatsEvents.py index 2a691582..51df3623 100644 --- a/server/src/uds/models/StatsEvents.py +++ b/server/src/uds/models/StatsEvents.py @@ -40,7 +40,7 @@ from uds.models.Util import getSqlDatetime import logging -__updated__ = '2015-07-02' +__updated__ = '2016-01-19' logger = logging.getLogger(__name__) @@ -76,7 +76,10 @@ class StatsEvents(models.Model): Note: if someone cant get this more optimized, please, contribute it! ''' - fltr = StatsEvents.objects.filter(event_type=event_type) + if isinstance(event_type, (list, tuple)): + fltr = StatsEvents.objects.filter(event_type__in=event_type) + else: + fltr = StatsEvents.objects.filter(event_type=event_type) if type(owner_type) in (list, tuple): fltr = fltr.filter(owner_type__in=owner_type) diff --git a/server/src/uds/models/TicketStore.py b/server/src/uds/models/TicketStore.py index d0922d09..431fac09 100644 --- a/server/src/uds/models/TicketStore.py +++ b/server/src/uds/models/TicketStore.py @@ -44,7 +44,7 @@ import logging logger = logging.getLogger(__name__) -__updated__ = '2015-05-25' +__updated__ = '2016-01-14' class TicketStore(UUIDModel): @@ -83,12 +83,19 @@ class TicketStore(UUIDModel): @staticmethod def create(data, validator=None, validity=DEFAULT_VALIDITY): + ''' + validity is in seconds + ''' if validator is not None: validator = pickle.dumps(validator) return TicketStore.objects.create(stamp=getSqlDatetime(), data=pickle.dumps(data), validator=validator, validity=validity).uuid @staticmethod def store(uuid, data, validator=None, validity=DEFAULT_VALIDITY): + ''' + Stores an ticketstore. If one with this uuid already exists, replaces it. Else, creates a new one + validity is in seconds + ''' if validator is not None: validator = pickle.dumps(validator) try: diff --git a/server/src/uds/reports/stats/__init__.py b/server/src/uds/reports/stats/__init__.py index 0729f040..e58f864e 100644 --- a/server/src/uds/reports/stats/__init__.py +++ b/server/src/uds/reports/stats/__init__.py @@ -34,3 +34,4 @@ from .usage import StatsReportUsage from .login import StatsReportLogin, StatsReportLoginCSV from .pool_performance import PoolPerformanceReport +from .usage_by_pool import UsageByPool diff --git a/server/src/uds/reports/stats/usage_by_pool.py b/server/src/uds/reports/stats/usage_by_pool.py new file mode 100644 index 00000000..2f9a6911 --- /dev/null +++ b/server/src/uds/reports/stats/usage_by_pool.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2015 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' +from __future__ import unicode_literals + +from django.utils.translation import ugettext, ugettext_lazy as _ + +from uds.core.ui.UserInterface import gui +from uds.core.reports.tools import UDSGeraldoReport +from uds.core.util.stats import events + + +import StringIO +import csv + + +from .base import StatsReport + +from uds.core.util import tools +from uds.models import ServicePool +from geraldo.generators.pdf import PDFGenerator +from geraldo import ReportBand, ObjectValue +from reportlab.lib.units import cm, mm + +import datetime +import logging + +logger = logging.getLogger(__name__) + +__updated__ = '2016-01-19' + +# several constants as Width height, margins, .. +WIDTH, HEIGHT = 1800, 1000 +GERALDO_WIDTH = 120 * mm +GERALDO_HEIGHT = GERALDO_WIDTH * HEIGHT / WIDTH + + +class UsersReport(UDSGeraldoReport): + title = '' + author = 'UDS' + + class band_detail(ReportBand): + height = 0.5 * cm + elements = ( + ObjectValue(attribute_name='date', left=0.5 * cm, style={'fontName': 'Helvetica', 'fontSize': 8}), + ObjectValue(attribute_name='name', left=5.5 * cm, style={'fontName': 'Helvetica', 'fontSize': 8}), + ObjectValue(attribute_name='time', left=12 * cm, style={'fontName': 'Helvetica', 'fontSize': 8}), + ) + + +class UsageByPool(StatsReport): + filename = 'pools_usage.pdf' + name = _('Pools usage by users') # Report name + description = _('Pools usage by user report') # Report description + uuid = '38ec12dc-beaf-11e5-bd0a-10feed05884b' + + # Input fields + pool = gui.ChoiceField( + order=1, + label=_('Pool'), + tooltip=_('Pool for report'), + required=True + ) + + startDate = gui.DateField( + order=2, + label=_('Starting date'), + tooltip=_('starting date for report'), + defvalue=datetime.date.min, + required=True + ) + + endDate = gui.DateField( + order=3, + label=_('Finish date'), + tooltip=_('finish date for report'), + defvalue=datetime.date.max, + required=True + ) + + def initialize(self, values): + pass + + def initGui(self): + logger.debug('Initializing gui') + vals = [ + gui.choiceItem(v.uuid, v.name) for v in ServicePool.objects.all() + ] + self.pool.setValues(vals) + + def getData(self): + # Generate the sampling intervals and get dataUsers from db + start = self.startDate.stamp() + end = self.endDate.stamp() + logger.debug(self.pool.value) + pool = ServicePool.objects.get(uuid=self.pool.value) + + items = events.statsManager().getEvents(events.OT_DEPLOYED, (events.ET_LOGIN, events.ET_LOGOUT), owner_id=pool.id, since=start, to=end).order_by('stamp') + + logins = {} + data = [] + for i in items: + if '\\' in i.fld1: + continue + + if i.event_type == events.ET_LOGIN: + logins[i.fld4] = i.stamp + else: + if i.fld4 in logins: + stamp = logins[i.fld4] + del logins[i.fld4] + total = i.stamp - stamp + data.append({ + 'name': i.fld4, + 'date': datetime.datetime.fromtimestamp(stamp), + 'time': total + }) + + logger.debug('data') + + return data, pool.name + + def generate(self): + items, poolName = self.getData() + + output = StringIO.StringIO() + + report = UsersReport(queryset=items) + report.title = _('Users usage list for {}').format(poolName) + report.generate_by(PDFGenerator, filename=output) + return output.getvalue() + + +class UsageByPoolCSV(UsageByPool): + filename = 'usage.csv' + mime_type = 'text/csv' # Report returns pdfs by default, but could be anything else + uuid = '5f7f0844-beb1-11e5-9a96-10feed05884b' + encoded = False + + # Input fields + pool = UsageByPool.pool + startDate = UsageByPool.startDate + endDate = UsageByPool.endDate + + def generate(self): + output = StringIO.StringIO() + writer = csv.writer(output) + + reportData = self.getRangeData()[2] + + writer.writerow([ugettext('Pool'), ugettext('Date range'), ugettext('Users'), ugettext('Accesses')]) + + for v in reportData: + writer.writerow([v['name'], v['date'], v['users'], v['accesses']]) + + return output.getvalue()