2018-08-16 16:57:37 -05:00
# -*- coding: utf-8 -*-
""" A base class to be derived in possible new sessions for TWBlue and services. """
2019-06-06 11:52:23 -05:00
import os
2018-08-16 16:57:37 -05:00
import paths
import output
import time
import sound
import logging
import config_utils
2021-06-23 13:40:21 -05:00
import sqlitedict
2018-08-16 16:57:37 -05:00
import application
2018-11-22 13:35:19 -06:00
from . import session_exceptions as Exceptions
2018-08-16 16:57:37 -05:00
log = logging . getLogger ( " sessionmanager.session " )
class baseSession ( object ) :
2021-06-16 16:18:41 -05:00
""" toDo: Decorators does not seem to be working when using them in an inherited class. """
2018-08-16 16:57:37 -05:00
2021-06-16 16:18:41 -05:00
# Decorators.
2018-08-16 16:57:37 -05:00
2021-06-16 16:18:41 -05:00
def _require_login ( fn ) :
""" Decorator for checking if the user is logged in.
Some functions may need this to avoid making unneeded calls . """
def f ( self , * args , * * kwargs ) :
if self . logged == True :
fn ( self , * args , * * kwargs )
else :
raise Exceptions . NotLoggedSessionError ( " You are not logged in yet. " )
return f
2018-08-16 16:57:37 -05:00
2021-06-16 16:18:41 -05:00
def _require_configuration ( fn ) :
""" Check if the user has a configured session. """
def f ( self , * args , * * kwargs ) :
if self . settings != None :
fn ( self , * args , * * kwargs )
else :
raise Exceptions . NotConfiguredSessionError ( " Not configured. " )
return f
2018-08-16 16:57:37 -05:00
2021-06-16 16:18:41 -05:00
def __init__ ( self , session_id ) :
""" session_id (str): The name of the folder inside the config directory where the session is located. """
super ( baseSession , self ) . __init__ ( )
self . session_id = session_id
self . logged = False
self . settings = None
self . db = { }
2021-08-27 13:45:59 -05:00
# Config specification file.
self . config_spec = " conf.defaults "
2021-08-30 10:51:26 -05:00
# Session type.
self . type = " base "
2018-08-16 16:57:37 -05:00
2021-06-16 16:18:41 -05:00
@property
def is_logged ( self ) :
return self . logged
2018-08-16 16:57:37 -05:00
2021-06-16 16:18:41 -05:00
def get_configuration ( self ) :
""" Get settings for a session. """
file_ = " %s /session.conf " % ( self . session_id , )
log . debug ( " Creating config file %s " % ( file_ , ) )
2021-08-27 13:45:59 -05:00
self . settings = config_utils . load_config ( os . path . join ( paths . config_path ( ) , file_ ) , os . path . join ( paths . app_path ( ) , self . config_spec ) )
2021-06-16 16:18:41 -05:00
self . init_sound ( )
2021-06-25 23:35:33 -05:00
self . load_persistent_data ( )
2018-08-16 16:57:37 -05:00
2022-11-14 17:51:27 -06:00
def get_name ( self ) :
pass
2021-06-16 16:18:41 -05:00
def init_sound ( self ) :
try : self . sound = sound . soundSystem ( self . settings [ " sound " ] )
except : pass
2018-08-16 16:57:37 -05:00
2021-06-16 16:18:41 -05:00
@_require_configuration
def login ( self , verify_credentials = True ) :
pass
2018-08-16 16:57:37 -05:00
2021-06-16 16:18:41 -05:00
@_require_configuration
def authorise ( self ) :
pass
2018-08-16 16:57:37 -05:00
2021-06-26 15:16:50 -05:00
def get_sized_buffer ( self , buffer , size , reversed = False ) :
""" Returns a list with the amount of items specified by size. """
if isinstance ( buffer , list ) and size != - 1 and len ( buffer ) > size :
log . debug ( " Requesting {} items from a list of {} items. Reversed mode: {} " . format ( size , len ( buffer ) , reversed ) )
2021-06-27 02:32:28 -05:00
if reversed == True :
2021-06-26 15:16:50 -05:00
return buffer [ : size ]
2021-06-27 02:32:28 -05:00
else :
return buffer [ len ( buffer ) - size : ]
2021-06-26 15:16:50 -05:00
else :
return buffer
2021-06-25 23:35:33 -05:00
def save_persistent_data ( self ) :
2021-06-26 15:16:50 -05:00
""" Save the data to a persistent sqlite backed file. . """
2021-06-25 23:35:33 -05:00
dbname = os . path . join ( paths . config_path ( ) , str ( self . session_id ) , " cache.db " )
2021-06-26 15:16:50 -05:00
log . debug ( " Saving storage information... " )
2021-06-25 23:35:33 -05:00
# persist_size set to 0 means not saving data actually.
2021-06-16 16:18:41 -05:00
if self . settings [ " general " ] [ " persist_size " ] == 0 :
2021-06-25 23:35:33 -05:00
if os . path . exists ( dbname ) :
os . remove ( dbname )
2021-06-16 16:18:41 -05:00
return
2021-06-26 15:16:50 -05:00
# Let's check if we need to create a new SqliteDict object (when loading db in memory) or we just need to call to commit in self (if reading from disk).db.
# If we read from disk, we cannot modify the buffer size here as we could damage the app's integrity.
# We will modify buffer's size (managed by persist_size) upon loading the db into memory in app startup.
2021-06-27 02:32:28 -05:00
if self . settings [ " general " ] [ " load_cache_in_memory " ] and isinstance ( self . db , dict ) :
2021-06-26 15:16:50 -05:00
log . debug ( " Opening database to dump memory contents... " )
2021-06-26 01:52:07 -05:00
db = sqlitedict . SqliteDict ( dbname , ' c ' )
for k in self . db . keys ( ) :
2021-06-27 02:32:28 -05:00
sized_buff = self . get_sized_buffer ( self . db [ k ] , self . settings [ " general " ] [ " persist_size " ] , self . settings [ " general " ] [ " reverse_timelines " ] )
db [ k ] = sized_buff
db . commit ( blocking = True )
2021-06-26 01:52:07 -05:00
db . close ( )
2021-06-26 15:16:50 -05:00
log . debug ( " Data has been saved in the database. " )
2021-06-26 01:52:07 -05:00
else :
try :
2021-06-26 15:16:50 -05:00
log . debug ( " Syncing new data to disk... " )
2021-06-27 02:32:28 -05:00
if hasattr ( self . db , " commit " ) :
self . db . commit ( )
2021-06-26 01:52:07 -05:00
except :
output . speak ( _ ( " An exception occurred while saving the {app} database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the {app} developers. " ) . format ( app = application . name ) , True )
log . exception ( " Exception while saving {} " . format ( dbname ) )
os . remove ( dbname )
2021-06-16 16:18:41 -05:00
2021-06-25 23:35:33 -05:00
def load_persistent_data ( self ) :
""" Import data from a database file from user config. """
2021-06-26 15:16:50 -05:00
log . debug ( " Loading storage data... " )
2021-06-25 23:35:33 -05:00
dbname = os . path . join ( paths . config_path ( ) , str ( self . session_id ) , " cache.db " )
# If persist_size is set to 0, we should remove the db file as we are no longer going to save anything.
2021-06-16 16:18:41 -05:00
if self . settings [ " general " ] [ " persist_size " ] == 0 :
2021-06-25 23:35:33 -05:00
if os . path . exists ( dbname ) :
os . remove ( dbname )
# Let's return from here, as we are not loading anything.
2021-06-16 16:18:41 -05:00
return
2021-06-25 23:35:33 -05:00
# try to load the db file.
2021-06-16 16:18:41 -05:00
try :
2021-06-26 15:16:50 -05:00
log . debug ( " Opening database... " )
2021-06-26 01:52:07 -05:00
db = sqlitedict . SqliteDict ( os . path . join ( paths . config_path ( ) , dbname ) , ' c ' )
# If load_cache_in_memory is set to true, we will load the whole database into memory for faster access.
# This is going to be faster when retrieving specific objects, at the cost of more memory.
# Setting this to False will read the objects from database as they are needed, which might be slower for bigger datasets.
if self . settings [ " general " ] [ " load_cache_in_memory " ] :
2021-06-26 15:16:50 -05:00
log . debug ( " Loading database contents into memory... " )
2021-06-26 01:52:07 -05:00
for k in db . keys ( ) :
self . db [ k ] = db [ k ]
2021-06-27 02:32:28 -05:00
db . commit ( blocking = True )
2021-06-26 01:52:07 -05:00
db . close ( )
2021-06-26 15:16:50 -05:00
log . debug ( " Contents were loaded successfully. " )
2021-06-26 01:52:07 -05:00
else :
2021-06-26 15:16:50 -05:00
log . debug ( " Instantiating database from disk. " )
2021-06-26 01:52:07 -05:00
self . db = db
2021-06-26 15:16:50 -05:00
# We must make sure we won't load more than the amount of buffer specified.
log . debug ( " Checking if we will load all content... " )
for k in self . db . keys ( ) :
2021-06-27 02:32:28 -05:00
sized_buffer = self . get_sized_buffer ( self . db [ k ] , self . settings [ " general " ] [ " persist_size " ] , self . settings [ " general " ] [ " reverse_timelines " ] )
self . db [ k ] = sized_buffer
2021-06-23 13:40:21 -05:00
if self . db . get ( " cursors " ) == None :
cursors = dict ( direct_messages = - 1 )
self . db [ " cursors " ] = cursors
2021-06-16 16:18:41 -05:00
except :
2021-06-25 23:35:33 -05:00
output . speak ( _ ( " An exception occurred while loading the {app} database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the {app} developers. " ) . format ( app = application . name ) , True )
log . exception ( " Exception while loading {} " . format ( dbname ) )
2021-06-16 16:18:41 -05:00
try :
2021-06-25 23:35:33 -05:00
os . remove ( dbname )
2021-06-16 16:18:41 -05:00
except :
pass
2018-08-16 16:57:37 -05:00