Add a first version of synchronisation
Remove redux-offline dependency
make the redux state fully immutable
TODO: better error management
TODO: make syncronization work automatically
import datetime
import logging
from auditlog.models import LogEntry
from django.utils import timezone
from notes.models import Note, Session
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView
logger = logging.getLogger(__name__)
class ListLogsView(APIView):
"""
View to list tle log of changes on Note and Sessions.
* Only registered users are able to access this view.
* the results are filtered by connected user
"""
permission_classes = (permissions.IsAuthenticated,)
def __filter_object(self, model, user, modified_since, client_id):
"""
Log entries are filtered by model, actor and timestamp.
If a client id is given, log entries from this client are ignored.
"""
log_entries = LogEntry.objects.get_for_model(model).filter(actor=user)
if modified_since:
log_entries = log_entries.filter(timestamp__gte=modified_since)
if client_id:
log_entries = log_entries.exclude(client=client_id)
return log_entries.order_by('timestamp')
def __process_log_entries(self, model, user, modified_since, client_id):
'''
Process log entries
'''
log_entries = self.__filter_object(model, user, modified_since, client_id)
logger.debug("LOG ENTRies %r", list(log_entries))
res = {}
for log_entry in log_entries:
ext_id = log_entry.additional_data.get('ext_id')
if not ext_id:
continue
sync_entry = res.get(ext_id, {})
new_action = {
(None , LogEntry.Action.CREATE): LogEntry.Action.CREATE,
(LogEntry.Action.CREATE, LogEntry.Action.CREATE): LogEntry.Action.CREATE,
(LogEntry.Action.UPDATE, LogEntry.Action.CREATE): LogEntry.Action.UPDATE,
(LogEntry.Action.DELETE, LogEntry.Action.CREATE): LogEntry.Action.UPDATE,
(None , LogEntry.Action.UPDATE): LogEntry.Action.UPDATE,
(LogEntry.Action.CREATE, LogEntry.Action.UPDATE): LogEntry.Action.CREATE,
(LogEntry.Action.UPDATE, LogEntry.Action.UPDATE): LogEntry.Action.UPDATE,
(LogEntry.Action.DELETE, LogEntry.Action.UPDATE): LogEntry.Action.DELETE,
(None , LogEntry.Action.DELETE): LogEntry.Action.DELETE,
(LogEntry.Action.CREATE, LogEntry.Action.DELETE): None,
(LogEntry.Action.UPDATE, LogEntry.Action.DELETE): LogEntry.Action.DELETE,
(LogEntry.Action.DELETE, LogEntry.Action.DELETE): LogEntry.Action.DELETE,
} [(sync_entry.get('action'), log_entry.action)]
if new_action is None:
del res[ext_id]
else:
res[ext_id] = {
'type': log_entry.content_type.model,
'ext_id': ext_id,
'action': new_action,
'timestamp': log_entry.timestamp
}
return res
def get(self, request, format=None):
"""
Return an aggregations of all changes.
"""
modified_since_str = request.query_params.get('modified_since', None)
modified_since = None
if modified_since_str is not None:
modified_since = datetime.datetime.fromtimestamp(
float(modified_since_str),
timezone.utc
)
user = request.user
client_id = request.META.get('HTTP_AUDITLOG_CLIENT', None)
res_sessions = self.__process_log_entries(Session, user, modified_since, client_id)
res_notes = self.__process_log_entries(Note, user, modified_since, client_id)
return Response({
'sessions': res_sessions.values(),
'notes': res_notes.values()
})