bug 592590, activity log fun for all ages
This commit is contained in:
Родитель
e6eabb946f
Коммит
3e84bc7a07
|
@ -7,6 +7,7 @@ import commonware.log
|
|||
from product_details import firefox_versions, thunderbird_versions
|
||||
from tower import ugettext_lazy as _
|
||||
|
||||
from .log import LOG, LOG_BY_ID, LOG_KEEP
|
||||
|
||||
# Every app should have its own logger.
|
||||
log = commonware.log.getLogger('z.amo')
|
||||
|
|
|
@ -10,7 +10,7 @@ import commonware.log
|
|||
import amo
|
||||
from bandwagon.models import Collection
|
||||
from cake.models import Session
|
||||
from devhub.models import AddonLog, LOG as ADDONLOG
|
||||
from devhub.models import ActivityLog
|
||||
from files.models import TestResult, TestResultCache
|
||||
from sharing import SERVICES_LIST
|
||||
from stats.models import AddonShareCount, Contribution
|
||||
|
@ -91,24 +91,9 @@ def gc(test_result=True):
|
|||
created__lt=six_days_ago).delete()
|
||||
|
||||
log.debug('Removing old entries from add-on news feeds.')
|
||||
keep = (
|
||||
ADDONLOG['Create Add-on'],
|
||||
ADDONLOG['Add User with Role'],
|
||||
ADDONLOG['Remove User with Role'],
|
||||
ADDONLOG['Set Inactive'],
|
||||
ADDONLOG['Unset Inactive'],
|
||||
ADDONLOG['Change Status'],
|
||||
ADDONLOG['Add Version'],
|
||||
ADDONLOG['Delete Version'],
|
||||
ADDONLOG['Approve Version'],
|
||||
ADDONLOG['Retain Version'],
|
||||
ADDONLOG['Escalate Version'],
|
||||
ADDONLOG['Request Version'],
|
||||
ADDONLOG['Add Recommended'],
|
||||
ADDONLOG['Remove Recommended'],
|
||||
)
|
||||
AddonLog.objects.filter(created__lt=three_months_ago).exclude(
|
||||
type__in=keep).delete()
|
||||
|
||||
ActivityLog.objects.filter(created__lt=three_months_ago).exclude(
|
||||
action__in=amo.LOG_KEEP).delete()
|
||||
|
||||
log.debug('Cleaning up anonymous collections.')
|
||||
Collection.objects.filter(created__lt=two_days_ago,
|
||||
|
|
|
@ -66,11 +66,10 @@
|
|||
},
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "devhub.addonlog",
|
||||
"model": "devhub.activitylog",
|
||||
"fields": {
|
||||
"created": "2000-01-01",
|
||||
"addon": 59,
|
||||
"type": 99
|
||||
"action": 99
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
from collections import namedtuple
|
||||
|
||||
from celery.datastructures import AttributeDict
|
||||
from tower import ugettext as _
|
||||
|
||||
__all__ = ('LOG', 'LOG_BY_ID', 'LOG_KEEP',)
|
||||
|
||||
_LOG = namedtuple('LOG', 'id format')
|
||||
|
||||
|
||||
class CREATE_ADDON:
|
||||
id = 1
|
||||
format = _('{user.name} created addon {addon.name}')
|
||||
keep = True
|
||||
|
||||
|
||||
class EDIT_PROPERTIES:
|
||||
id = 2
|
||||
format = _('{user.name} edited addon {addon.name} properties')
|
||||
|
||||
|
||||
class EDIT_DESCRIPTIONS:
|
||||
id = 3
|
||||
format = _('{user.name} edited addon {addon.name} description')
|
||||
|
||||
|
||||
class EDIT_CATEGORIES:
|
||||
id = 4
|
||||
format = _('{user.name} edited categories for {addon.name}')
|
||||
|
||||
|
||||
class ADD_USER_WITH_ROLE:
|
||||
id = 5
|
||||
format = _('{user.name} added {0.name} to '
|
||||
'addon {addon.name} with role {1}')
|
||||
keep = True
|
||||
|
||||
|
||||
class REMOVE_USER_WITH_ROLE:
|
||||
id = 6
|
||||
# L10n: {0} is the user being removed, {1} is their role.
|
||||
format = _('{user.name} removed {0} with role {1}')
|
||||
keep = True
|
||||
|
||||
|
||||
class EDIT_CONTRIBUTIONS:
|
||||
id = 7
|
||||
format = _('{user.name} edited contributions for {addon.name}')
|
||||
|
||||
|
||||
class SET_INACTIVE:
|
||||
id = 8
|
||||
format = _('{user.name} set addon {addon.name} inactive')
|
||||
keep = True
|
||||
|
||||
|
||||
class UNSET_INACTIVE:
|
||||
id = 9
|
||||
format = _('{user.name} activated addon {addon.name}')
|
||||
keep = True
|
||||
|
||||
|
||||
class SET_PUBLIC_STATS:
|
||||
id = 10
|
||||
format = _('{user.name} set stats public for {addon}')
|
||||
keep = True
|
||||
|
||||
|
||||
class UNSET_PUBLIC_STATS:
|
||||
id = 11
|
||||
format = _('{user.name} set stats private for {addon}')
|
||||
keep = True
|
||||
|
||||
|
||||
class CHANGE_STATUS:
|
||||
id = 12
|
||||
# L10n: {0} is the status
|
||||
format = _('{user.name} changed {addon} status to {0}')
|
||||
keep = True
|
||||
|
||||
|
||||
class ADD_PREVIEW:
|
||||
id = 13
|
||||
format = _('{user.name} added preview to {addon}')
|
||||
|
||||
|
||||
class EDIT_PREVIEW:
|
||||
id = 14
|
||||
format = _('{user.name} edited preview for {addon}')
|
||||
|
||||
|
||||
class DELETE_PREVIEW:
|
||||
id = 15
|
||||
format = _('{user.name} deleted preview from {addon}')
|
||||
|
||||
|
||||
class ADD_VERSION:
|
||||
id = 16
|
||||
format = _('{user.name} added version {0.version} to {addon}')
|
||||
keep = True
|
||||
|
||||
|
||||
class EDIT_VERSION:
|
||||
id = 17
|
||||
format = _('{user.name} edited version {0.version} of {addon}')
|
||||
|
||||
|
||||
class DELETE_VERSION:
|
||||
id = 18
|
||||
format = _('{user.name} deleted version {0.version} from {addon}')
|
||||
keep = True
|
||||
|
||||
|
||||
class ADD_FILE_TO_VERSION:
|
||||
id = 19
|
||||
format = _('{user.name} added file {0.name} to '
|
||||
'version {0.version} of {addon}')
|
||||
|
||||
|
||||
class DELETE_FILE_FROM_VERSION:
|
||||
id = 20
|
||||
format = _('{user.name} deleted file {0.name} '
|
||||
'from {addon} version {0.version}')
|
||||
|
||||
|
||||
class APPROVE_VERSION:
|
||||
id = 21
|
||||
format = _('Version {0.version} of {addon} approved')
|
||||
keep = True
|
||||
|
||||
|
||||
class RETAIN_VERSION:
|
||||
id = 22
|
||||
format = _('{user.name} retained version {0.version} of {addon}')
|
||||
keep = True
|
||||
|
||||
|
||||
class ESCALATE_VERSION:
|
||||
id = 23
|
||||
# L10n: {0.version} is the version of an addon.
|
||||
format = _('{user.name} escalated review of {addon} {0.version}')
|
||||
keep = True
|
||||
|
||||
|
||||
class REQUEST_VERSION:
|
||||
id = 24
|
||||
# L10n: {0.version} is the version of an addon.
|
||||
format = _('{user.name} requested more information regarding '
|
||||
'{addon} {0.version}')
|
||||
keep = True
|
||||
|
||||
|
||||
class ADD_TAG:
|
||||
id = 25
|
||||
# L10n: {0} is the tag name.
|
||||
format = _('{user.name} added tag {0} to {addon}')
|
||||
|
||||
|
||||
class REMOVE_TAG:
|
||||
id = 26
|
||||
# L10n: {0} is the tag name.
|
||||
format = _('{user.name} removed tag {0} from {addon}')
|
||||
|
||||
|
||||
class ADD_TO_COLLECTION:
|
||||
id = 27
|
||||
format = _('{user.name} added addon {addon} to a collection {0.name}')
|
||||
|
||||
|
||||
class REMOVE_FROM_COLLECTION:
|
||||
id = 28
|
||||
forma = _('{user.name} removed addon {addon} from a collection {0.name}')
|
||||
|
||||
|
||||
class ADD_REVIEW:
|
||||
id = 29
|
||||
format = _('{user.name} wrote a review about {addon}')
|
||||
|
||||
|
||||
class ADD_RECOMMENDED_CATEGORY:
|
||||
id = 31
|
||||
# L10n: {0} is a category name.
|
||||
format = _('{addon} featured in {0}')
|
||||
|
||||
|
||||
class REMOVE_RECOMMENDED_CATEGORY:
|
||||
id = 32
|
||||
# L10n: {0} is a category name.
|
||||
format = _('{addon} no longer featured in {0}')
|
||||
|
||||
|
||||
class ADD_RECOMMENDED:
|
||||
id = 33
|
||||
format = _('{addon} is now featured')
|
||||
keep = True
|
||||
|
||||
|
||||
class REMOVE_RECOMMENDED:
|
||||
id = 34
|
||||
format = _('{addon} is no longer featured')
|
||||
keep = True
|
||||
|
||||
|
||||
class ADD_APPVERSION:
|
||||
id = 35
|
||||
# L10n: {0} is the application, {1.min/max} is the min/max version of the
|
||||
# app
|
||||
format = _('addon now supports {0} {1.min}-{1.max}')
|
||||
|
||||
|
||||
class CUSTOM_TEXT:
|
||||
id = 98
|
||||
format = '{0}'
|
||||
|
||||
|
||||
class CUSTOM_HTML:
|
||||
id = 99
|
||||
format = '{0}'
|
||||
|
||||
|
||||
LOGS = (CREATE_ADDON, EDIT_PROPERTIES, EDIT_DESCRIPTIONS, EDIT_CATEGORIES,
|
||||
ADD_USER_WITH_ROLE, REMOVE_USER_WITH_ROLE, EDIT_CONTRIBUTIONS,
|
||||
SET_INACTIVE, UNSET_INACTIVE, SET_PUBLIC_STATS, UNSET_PUBLIC_STATS,
|
||||
CHANGE_STATUS, ADD_PREVIEW, EDIT_PREVIEW, DELETE_PREVIEW,
|
||||
ADD_VERSION, EDIT_VERSION, DELETE_VERSION, ADD_FILE_TO_VERSION,
|
||||
DELETE_FILE_FROM_VERSION, APPROVE_VERSION, RETAIN_VERSION,
|
||||
ESCALATE_VERSION, REQUEST_VERSION, ADD_TAG, REMOVE_TAG,
|
||||
ADD_TO_COLLECTION, REMOVE_FROM_COLLECTION, ADD_REVIEW,
|
||||
ADD_RECOMMENDED_CATEGORY, REMOVE_RECOMMENDED_CATEGORY, ADD_RECOMMENDED,
|
||||
REMOVE_RECOMMENDED, ADD_APPVERSION, CUSTOM_TEXT, CUSTOM_HTML,
|
||||
)
|
||||
LOG_BY_ID = dict((l.id, l) for l in LOGS)
|
||||
LOG = AttributeDict((l.__name__, l) for l in LOGS)
|
||||
LOG_KEEP = (l.id for l in LOGS if hasattr(l, 'keep'))
|
|
@ -3,7 +3,7 @@ import test_utils
|
|||
|
||||
from bandwagon.models import Collection
|
||||
from cake.models import Session
|
||||
from devhub.models import AddonLog
|
||||
from devhub.models import ActivityLog
|
||||
from files.models import TestResult, TestResultCache
|
||||
from stats.models import AddonShareCount, Contribution
|
||||
from amo.cron import gc
|
||||
|
@ -16,7 +16,7 @@ class GarbageTest(test_utils.TestCase):
|
|||
"This fixture is expired data that should just get cleaned up."
|
||||
eq_(Collection.objects.all().count(), 1)
|
||||
eq_(Session.objects.all().count(), 1)
|
||||
eq_(AddonLog.objects.all().count(), 1)
|
||||
eq_(ActivityLog.objects.all().count(), 1)
|
||||
eq_(TestResult.objects.all().count(), 1)
|
||||
eq_(TestResultCache.objects.all().count(), 1)
|
||||
eq_(AddonShareCount.objects.all().count(), 1)
|
||||
|
@ -24,7 +24,7 @@ class GarbageTest(test_utils.TestCase):
|
|||
gc(test_result=False)
|
||||
eq_(Collection.objects.all().count(), 0)
|
||||
eq_(Session.objects.all().count(), 0)
|
||||
eq_(AddonLog.objects.all().count(), 0)
|
||||
eq_(ActivityLog.objects.all().count(), 0)
|
||||
# XXX(davedash): this isn't working in testing.
|
||||
# eq_(TestResult.objects.all().count(), 0)
|
||||
eq_(TestResultCache.objects.all().count(), 0)
|
||||
|
|
|
@ -1,60 +1,18 @@
|
|||
from datetime import datetime
|
||||
import json
|
||||
|
||||
from django.db import models
|
||||
|
||||
import commonware.log
|
||||
|
||||
import amo
|
||||
import amo.models
|
||||
from addons.models import Addon
|
||||
from users.models import UserProfile
|
||||
from translations.fields import TranslatedField
|
||||
|
||||
|
||||
LOG = {
|
||||
'Create Add-on': 1,
|
||||
'Edit Properties': 2,
|
||||
'Edit Descriptions': 3,
|
||||
'Edit Categories': 4,
|
||||
'Add User with Role': 5,
|
||||
'Remove User with Role': 6,
|
||||
'Edit Contributions': 7,
|
||||
|
||||
'Set Inactive': 8,
|
||||
'Unset Inactive': 9,
|
||||
'Set Public Stats': 10,
|
||||
'Unset Public Stats': 11,
|
||||
'Change Status': 12,
|
||||
|
||||
'Add Preview': 13,
|
||||
'Edit Preview': 14,
|
||||
'Delete Preview': 15,
|
||||
|
||||
'Add Version': 16,
|
||||
'Edit Version': 17,
|
||||
'Delete Version': 18,
|
||||
'Add File to Version': 19,
|
||||
'Delete File from Version': 20,
|
||||
|
||||
'Approve Version': 21,
|
||||
'Retain Version': 22,
|
||||
'Escalate Version': 23,
|
||||
'Request Version': 24,
|
||||
|
||||
'Add Tag': 25,
|
||||
'Remove Tag': 26,
|
||||
|
||||
'Add to Collection': 27,
|
||||
'Remove from Collection': 28,
|
||||
|
||||
'Add Review': 29,
|
||||
|
||||
'Add Recommended Category': 31,
|
||||
'Remove Recommended Category': 32,
|
||||
|
||||
'Add Recommended': 33,
|
||||
'Remove Recommended': 34,
|
||||
|
||||
'Add Appversion': 35,
|
||||
|
||||
'Custom Text': 98,
|
||||
'Custom HTML': 99,
|
||||
}
|
||||
log = commonware.log.getLogger('devhub')
|
||||
|
||||
|
||||
class HubPromo(amo.models.ModelBase):
|
||||
|
@ -95,8 +53,139 @@ class HubEvent(amo.models.ModelBase):
|
|||
return ['*/developers*']
|
||||
|
||||
|
||||
class AddonLog(models.Model):
|
||||
TYPES = [(value, key) for key, value in LOG.items()]
|
||||
class AddonLog(amo.models.ModelBase):
|
||||
"""
|
||||
This table is for indexing the activity log by addon.
|
||||
"""
|
||||
addon = models.ForeignKey(Addon)
|
||||
activity_log = models.ForeignKey('ActivityLog')
|
||||
|
||||
class Meta:
|
||||
db_table = 'log_activity_addon'
|
||||
ordering = ('-created',)
|
||||
|
||||
|
||||
class UserLog(amo.models.ModelBase):
|
||||
"""
|
||||
This table is for indexing the activity log by user.
|
||||
Note: This includes activity performed unto the user.
|
||||
"""
|
||||
activity_log = models.ForeignKey('ActivityLog')
|
||||
user = models.ForeignKey(UserProfile)
|
||||
|
||||
class Meta:
|
||||
db_table = 'log_activity_user'
|
||||
ordering = ('-created',)
|
||||
|
||||
|
||||
class ActivityLogManager(amo.models.ManagerBase):
|
||||
def for_addon(self, addon, limit=20, offset=0):
|
||||
vals = (AddonLog.objects.filter(addon=addon)[offset:limit]
|
||||
.values_list('activity_log', flat=True))
|
||||
return self.filter(pk__in=list(vals))
|
||||
|
||||
def for_user(self, user, limit=20, offset=0):
|
||||
vals = (UserLog.objects.filter(user=user)[offset:limit]
|
||||
.values_list('activity_log', flat=True))
|
||||
return self.filter(pk__in=list(vals))
|
||||
|
||||
|
||||
class ActivityLog(amo.models.ModelBase):
|
||||
TYPES = [(value, key) for key, value in amo.LOG.items()]
|
||||
user = models.ForeignKey('users.UserProfile', null=True)
|
||||
action = models.SmallIntegerField(choices=TYPES, db_index=True)
|
||||
_arguments = models.TextField(blank=True, db_column='arguments')
|
||||
objects = ActivityLogManager()
|
||||
|
||||
@property
|
||||
def arguments(self):
|
||||
|
||||
try:
|
||||
# d is a structure:
|
||||
# ``d = [{'addons.addon'=12}, {'addons.addon'=1}, ... ]``
|
||||
d = json.loads(self._arguments)
|
||||
except:
|
||||
log.debug('unserializing data from addon_log failed: %s' % self.id)
|
||||
return None
|
||||
|
||||
objs = []
|
||||
for item in d:
|
||||
# item has only one element.
|
||||
model_name, pk = item.items()[0]
|
||||
if model_name == 'str':
|
||||
objs.append(pk)
|
||||
else:
|
||||
(app_label, model_name) = model_name.split('.')
|
||||
model = models.loading.get_model(app_label, model_name)
|
||||
objs.extend(model.objects.filter(pk=pk))
|
||||
|
||||
return objs
|
||||
|
||||
@arguments.setter
|
||||
def arguments(self, args=[]):
|
||||
"""
|
||||
Takes an object or a tuple of objects and serializes them and stores it
|
||||
in the db as a json string.
|
||||
"""
|
||||
if args is None:
|
||||
args = []
|
||||
|
||||
if not isinstance(args, (list, tuple)):
|
||||
args = (args,)
|
||||
|
||||
serialize_me = []
|
||||
|
||||
for arg in args:
|
||||
if isinstance(arg, str):
|
||||
serialize_me.append({'str': arg})
|
||||
else:
|
||||
serialize_me.append(dict(((unicode(arg._meta), arg.pk),)))
|
||||
|
||||
self._arguments = json.dumps(serialize_me)
|
||||
|
||||
@classmethod
|
||||
def log(cls, request, action, arguments=None):
|
||||
"""
|
||||
e.g. ActivityLog.log(request, amo.LOG.CREATE_ADDON, []),
|
||||
ActivityLog.log(request, amo.LOG.ADD_FILE_TO_VERSION,
|
||||
(file, version))
|
||||
"""
|
||||
al = cls(user=request.amo_user, action=action.id)
|
||||
al.arguments = arguments
|
||||
al.save()
|
||||
|
||||
if not isinstance(arguments, (list, tuple)):
|
||||
arguments = (arguments,)
|
||||
for arg in arguments:
|
||||
if isinstance(arg, Addon):
|
||||
AddonLog(addon=arg, activity_log=al).save()
|
||||
elif isinstance(arg, UserProfile):
|
||||
# Index by any user who is mentioned as an argument.
|
||||
UserLog(activity_log=al, user=arg).save()
|
||||
|
||||
# Index by every request user
|
||||
UserLog(activity_log=al, user=request.amo_user).save()
|
||||
|
||||
# TODO(davedash): Support other types.
|
||||
def to_string(self, type='default'):
|
||||
log_type = amo.LOG_BY_ID[self.action]
|
||||
arguments = self.arguments
|
||||
addon = None
|
||||
for arg in arguments:
|
||||
if isinstance(arg, Addon) and not addon:
|
||||
addon = arg
|
||||
break
|
||||
return log_type.format.format(*arguments, user=self.user, addon=addon)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.to_string()
|
||||
|
||||
class Meta:
|
||||
db_table = 'log_activity'
|
||||
|
||||
|
||||
class LegacyAddonLog(models.Model):
|
||||
TYPES = [(value, key) for key, value in amo.LOG.items()]
|
||||
|
||||
addon = models.ForeignKey('addons.Addon', null=True, blank=True)
|
||||
user = models.ForeignKey('users.UserProfile', null=True)
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
from nose.tools import eq_
|
||||
from mock import Mock
|
||||
import test_utils
|
||||
|
||||
import amo
|
||||
from addons.models import Addon
|
||||
from devhub.models import ActivityLog
|
||||
from users.models import UserProfile
|
||||
|
||||
|
||||
class TestActivityLog(test_utils.TestCase):
|
||||
fixtures = ('base/addon_3615',)
|
||||
|
||||
def setUp(self):
|
||||
self.request = Mock()
|
||||
u = UserProfile(username='Joe CamelCase')
|
||||
u.save()
|
||||
self.request.amo_user = u
|
||||
|
||||
def test_basic(self):
|
||||
request = self.request
|
||||
a = Addon.objects.get()
|
||||
ActivityLog.log(request, amo.LOG['CREATE_ADDON'], a)
|
||||
entries = ActivityLog.objects.for_addon(a)
|
||||
eq_(len(entries), 1)
|
||||
eq_(entries[0].arguments[0], a)
|
||||
eq_(unicode(entries[0]),
|
||||
'Joe CamelCase created addon Delicious Bookmarks')
|
||||
|
||||
def test_json_failboat(self):
|
||||
request = self.request
|
||||
a = Addon.objects.get()
|
||||
ActivityLog.log(request, amo.LOG['CREATE_ADDON'], a)
|
||||
entry = ActivityLog.objects.get()
|
||||
entry._arguments = 'failboat?'
|
||||
entry.save()
|
||||
eq_(entry.arguments, None)
|
||||
|
||||
def test_no_arguments(self):
|
||||
request = self.request
|
||||
ActivityLog.log(request, amo.LOG['CUSTOM_HTML'])
|
||||
entry = ActivityLog.objects.get()
|
||||
eq_(entry.arguments, [])
|
||||
|
||||
def test_output(self):
|
||||
request = self.request
|
||||
ActivityLog.log(request, amo.LOG['CUSTOM_TEXT'], 'hi there')
|
||||
entry = ActivityLog.objects.get()
|
||||
eq_(unicode(entry), 'hi there')
|
||||
|
||||
def test_user_log(self):
|
||||
request = self.request
|
||||
ActivityLog.log(request, amo.LOG['CUSTOM_TEXT'], 'hi there')
|
||||
entries = ActivityLog.objects.for_user(request.amo_user)
|
||||
eq_(len(entries), 1)
|
||||
|
||||
def test_user_log_as_argument(self):
|
||||
"""
|
||||
Tests that a user that has something done to them gets into the user
|
||||
log.
|
||||
"""
|
||||
request = self.request
|
||||
u = UserProfile(username='Marlboro Manatee')
|
||||
u.save()
|
||||
ActivityLog.log(request, amo.LOG['ADD_USER_WITH_ROLE'],
|
||||
(u, 'developer', Addon.objects.get()))
|
||||
entries = ActivityLog.objects.for_user(request.amo_user)
|
||||
eq_(len(entries), 1)
|
||||
entries = ActivityLog.objects.for_user(u)
|
||||
eq_(len(entries), 1)
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
CREATE TABLE `log_activity_addon` (
|
||||
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
|
||||
`created` datetime NOT NULL,
|
||||
`modified` datetime NOT NULL,
|
||||
`addon_id` integer NOT NULL,
|
||||
`activity_log_id` integer NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
||||
;
|
||||
ALTER TABLE `log_activity_addon` ADD CONSTRAINT `addon_id_refs_id_5bfa17d1` FOREIGN KEY (`addon_id`) REFERENCES `addons` (`id`);
|
||||
CREATE TABLE `log_activity_user` (
|
||||
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
|
||||
`created` datetime NOT NULL,
|
||||
`modified` datetime NOT NULL,
|
||||
`activity_log_id` integer NOT NULL,
|
||||
`user_id` integer NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
||||
;
|
||||
ALTER TABLE `log_activity_user` ADD CONSTRAINT `user_id_refs_id_e987c199` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`);
|
||||
CREATE TABLE `log_activity` (
|
||||
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
|
||||
`created` datetime NOT NULL,
|
||||
`modified` datetime NOT NULL,
|
||||
`user_id` integer,
|
||||
`action` smallint NOT NULL,
|
||||
`arguments` longtext NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
||||
;
|
||||
ALTER TABLE `log_activity` ADD CONSTRAINT `user_id_refs_id_3fa7a30a` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`);
|
||||
ALTER TABLE `log_activity_addon` ADD CONSTRAINT `activity_log_id_refs_id_9c20a926` FOREIGN KEY (`activity_log_id`) REFERENCES `log_activity` (`id`);
|
||||
ALTER TABLE `log_activity_user` ADD CONSTRAINT `activity_log_id_refs_id_4f8d99d4` FOREIGN KEY (`activity_log_id`) REFERENCES `log_activity` (`id`);
|
||||
CREATE INDEX `log_activity_addon_cc3d5937` ON `log_activity_addon` (`addon_id`);
|
||||
CREATE INDEX `log_activity_addon_3bf68f54` ON `log_activity_addon` (`activity_log_id`);
|
||||
CREATE INDEX `log_activity_user_3bf68f54` ON `log_activity_user` (`activity_log_id`);
|
||||
CREATE INDEX `log_activity_user_fbfc09f1` ON `log_activity_user` (`user_id`);
|
||||
CREATE INDEX `log_activity_fbfc09f1` ON `log_activity` (`user_id`);
|
||||
CREATE INDEX `log_activity_1bd4707b` ON `log_activity` (`action`);
|
Загрузка…
Ссылка в новой задаче