Add ActivityIntermediator table and refactor some of Activity model
- This has introduced a intermediatory table between object/target and the activity. This allows for multiple activities to be associated with one object/target. - This moves some of the methods off Activity model into a mixin which didn't need to interact with database things. - This also cleaned up the migrations as well as adding retroactive creation of activities for collection creation.
This commit is contained in:
parent
1c15126819
commit
ce46470c02
@ -29,7 +29,7 @@ from mediagoblin.db.extratypes import JSONEncoded, MutationDict
|
|||||||
from mediagoblin.db.migration_tools import (
|
from mediagoblin.db.migration_tools import (
|
||||||
RegisterMigration, inspect_table, replace_table_hack)
|
RegisterMigration, inspect_table, replace_table_hack)
|
||||||
from mediagoblin.db.models import (MediaEntry, Collection, MediaComment, User,
|
from mediagoblin.db.models import (MediaEntry, Collection, MediaComment, User,
|
||||||
Privilege)
|
Privilege, Generator)
|
||||||
from mediagoblin.db.extratypes import JSONEncoded, MutationDict
|
from mediagoblin.db.extratypes import JSONEncoded, MutationDict
|
||||||
|
|
||||||
|
|
||||||
@ -578,30 +578,6 @@ PRIVILEGE_FOUNDATIONS_v0 = [{'privilege_name':u'admin'},
|
|||||||
{'privilege_name':u'commenter'},
|
{'privilege_name':u'commenter'},
|
||||||
{'privilege_name':u'active'}]
|
{'privilege_name':u'active'}]
|
||||||
|
|
||||||
|
|
||||||
class Activity_R0(declarative_base()):
|
|
||||||
__tablename__ = "core__activities"
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
actor = Column(Integer, ForeignKey(User.id), nullable=False)
|
|
||||||
published = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
|
||||||
updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
|
||||||
verb = Column(Unicode, nullable=False)
|
|
||||||
content = Column(Unicode, nullable=False)
|
|
||||||
title = Column(Unicode, nullable=True)
|
|
||||||
target = Column(Integer, ForeignKey(User.id), nullable=True)
|
|
||||||
object_comment = Column(Integer, ForeignKey(MediaComment.id), nullable=True)
|
|
||||||
object_collection = Column(Integer, ForeignKey(Collection.id), nullable=True)
|
|
||||||
object_media = Column(Integer, ForeignKey(MediaEntry.id), nullable=True)
|
|
||||||
object_user = Column(Integer, ForeignKey(User.id), nullable=True)
|
|
||||||
|
|
||||||
class Generator(declarative_base()):
|
|
||||||
__tablename__ = "core__generators"
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
name = Column(Unicode, nullable=False)
|
|
||||||
published = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
|
||||||
updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
|
||||||
object_type = Column(Unicode, nullable=False)
|
|
||||||
|
|
||||||
# vR1 stands for "version Rename 1". This only exists because we need
|
# vR1 stands for "version Rename 1". This only exists because we need
|
||||||
# to deal with dropping some booleans and it's otherwise impossible
|
# to deal with dropping some booleans and it's otherwise impossible
|
||||||
# with sqlite.
|
# with sqlite.
|
||||||
@ -914,16 +890,90 @@ def revert_username_index(db):
|
|||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
|
class Generator_R0(declarative_base()):
|
||||||
|
__tablename__ = "core__generators"
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
name = Column(Unicode, nullable=False)
|
||||||
|
published = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
object_type = Column(Unicode, nullable=False)
|
||||||
|
|
||||||
|
class Activity_R0(declarative_base()):
|
||||||
|
__tablename__ = "core__activities"
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
actor = Column(Integer, ForeignKey(User.id), nullable=False)
|
||||||
|
published = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
verb = Column(Unicode, nullable=False)
|
||||||
|
content = Column(Unicode, nullable=False)
|
||||||
|
title = Column(Unicode, nullable=True)
|
||||||
|
target = Column(Integer, ForeignKey(User.id), nullable=True)
|
||||||
|
generator = Column(Integer, ForeignKey(Generator.id), nullable=True)
|
||||||
|
|
||||||
|
class ActivityIntermediator_R0(declarative_base()):
|
||||||
|
__tablename__ = "core__acitivity_intermediators"
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
type = Column(Integer, nullable=False)
|
||||||
|
|
||||||
@RegisterMigration(24, MIGRATIONS)
|
@RegisterMigration(24, MIGRATIONS)
|
||||||
def create_activity_table(db):
|
def activity_migration(db):
|
||||||
""" This will create the activity table """
|
"""
|
||||||
|
Creates everything to create activities in GMG
|
||||||
|
- Adds Activity, ActivityIntermediator and Generator table
|
||||||
|
- Creates GMG service generator for activities produced by the server
|
||||||
|
- Adds the activity_as_object and activity_as_target to objects/targets
|
||||||
|
- Retroactively adds activities for what we can acurately work out
|
||||||
|
"""
|
||||||
|
# Set constants we'll use later
|
||||||
|
FOREIGN_KEY = "core__acitivity_intermediators.id"
|
||||||
|
|
||||||
|
|
||||||
|
# Create the new tables.
|
||||||
Activity_R0.__table__.create(db.bind)
|
Activity_R0.__table__.create(db.bind)
|
||||||
Generator_R0.__table__.create(db.bind)
|
Generator_R0.__table__.create(db.bind)
|
||||||
|
ActivityIntermediator_R0.__table__.create(db.bind)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
|
||||||
|
# Initiate the tables we want to use later
|
||||||
|
metadata = MetaData(bind=db.bind)
|
||||||
|
user_table = inspect_table(metadata, "core__users")
|
||||||
|
generator_table = inspect_table(metadata, "core__generators")
|
||||||
|
collection_table = inspect_table(metadata, "core__collections")
|
||||||
|
media_entry_table = inspect_table(metadata, "core__media_entries")
|
||||||
|
media_comments_table = inspect_table(metadata, "core__media_comments")
|
||||||
|
|
||||||
|
|
||||||
|
# Create the foundations for Generator
|
||||||
|
db.execute(generator_table.insert().values(
|
||||||
|
name="GNU Mediagoblin",
|
||||||
|
object_type="service"
|
||||||
|
))
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
|
||||||
|
# Now we want to modify the tables which MAY have an activity at some point
|
||||||
|
as_object = Column("activity_as_object", Integer, ForeignKey(FOREIGN_KEY))
|
||||||
|
as_object.create(media_entry_table)
|
||||||
|
as_target = Column("activity_as_target", Integer, ForeignKey(FOREIGN_KEY))
|
||||||
|
as_target.create(media_entry_table)
|
||||||
|
|
||||||
|
as_object = Column("activity_as_object", Integer, ForeignKey(FOREIGN_KEY))
|
||||||
|
as_object.create(user_table)
|
||||||
|
as_target = Column("activity_as_target", Integer, ForeignKey(FOREIGN_KEY))
|
||||||
|
as_target.create(user_table)
|
||||||
|
|
||||||
|
as_object = Column("activity_as_object", Integer, ForeignKey(FOREIGN_KEY))
|
||||||
|
as_object.create(media_comments_table)
|
||||||
|
as_target = Column("activity_as_target", Integer, ForeignKey(FOREIGN_KEY))
|
||||||
|
as_target.create(media_comments_table)
|
||||||
|
|
||||||
|
as_object = Column("activity_as_object", Integer, ForeignKey(FOREIGN_KEY))
|
||||||
|
as_object.create(collection_table)
|
||||||
|
as_target = Column("activity_as_target", Integer, ForeignKey(FOREIGN_KEY))
|
||||||
|
as_target.create(collection_table)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
# Create the GNU MediaGoblin generator
|
|
||||||
gmg_generator = Generator(name="GNU MediaGoblin", object_type="service")
|
|
||||||
gmg_generator.save()
|
|
||||||
|
|
||||||
# Now we want to retroactively add what activities we can
|
# Now we want to retroactively add what activities we can
|
||||||
# first we'll add activities when people uploaded media.
|
# first we'll add activities when people uploaded media.
|
||||||
@ -932,10 +982,13 @@ def create_activity_table(db):
|
|||||||
verb="create",
|
verb="create",
|
||||||
actor=media.uploader,
|
actor=media.uploader,
|
||||||
published=media.created,
|
published=media.created,
|
||||||
object_media=media.id,
|
updated=media.created,
|
||||||
|
generator=gmg_generator.id
|
||||||
)
|
)
|
||||||
activity.generate_content()
|
activity.generate_content()
|
||||||
activity.save()
|
activity.save(set_updated=False)
|
||||||
|
activity.set_object(media)
|
||||||
|
media.save()
|
||||||
|
|
||||||
# Now we want to add all the comments people made
|
# Now we want to add all the comments people made
|
||||||
for comment in MediaComment.query.all():
|
for comment in MediaComment.query.all():
|
||||||
@ -943,8 +996,26 @@ def create_activity_table(db):
|
|||||||
verb="comment",
|
verb="comment",
|
||||||
actor=comment.author,
|
actor=comment.author,
|
||||||
published=comment.created,
|
published=comment.created,
|
||||||
|
updated=comment.created,
|
||||||
|
generator=gmg_generator.id
|
||||||
)
|
)
|
||||||
activity.generate_content()
|
activity.generate_content()
|
||||||
activity.save()
|
activity.save(set_updated=False)
|
||||||
|
activity.set_object(comment)
|
||||||
|
comment.save()
|
||||||
|
|
||||||
|
# Create 'create' activities for all collections
|
||||||
|
for collection in Collection.query.all():
|
||||||
|
activity = Activity_R0(
|
||||||
|
verb="create",
|
||||||
|
actor=collection.creator,
|
||||||
|
published=collection.created,
|
||||||
|
updated=collection.created,
|
||||||
|
generator=gmg_generator.id
|
||||||
|
)
|
||||||
|
activity.generate_content()
|
||||||
|
activity.save(set_updated=False)
|
||||||
|
activity.set_object(collection)
|
||||||
|
collection.save()
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
|
@ -39,6 +39,7 @@ from mediagoblin.tools import common, licenses
|
|||||||
from mediagoblin.tools.pluginapi import hook_handle
|
from mediagoblin.tools.pluginapi import hook_handle
|
||||||
from mediagoblin.tools.text import cleaned_markdown_conversion
|
from mediagoblin.tools.text import cleaned_markdown_conversion
|
||||||
from mediagoblin.tools.url import slugify
|
from mediagoblin.tools.url import slugify
|
||||||
|
from mediagoblin.tools.translate import pass_to_ugettext as _
|
||||||
|
|
||||||
|
|
||||||
class UserMixin(object):
|
class UserMixin(object):
|
||||||
@ -363,3 +364,109 @@ class CollectionItemMixin(object):
|
|||||||
Run through Markdown and the HTML cleaner.
|
Run through Markdown and the HTML cleaner.
|
||||||
"""
|
"""
|
||||||
return cleaned_markdown_conversion(self.note)
|
return cleaned_markdown_conversion(self.note)
|
||||||
|
|
||||||
|
class ActivityMixin(object):
|
||||||
|
|
||||||
|
VALID_VERBS = ["add", "author", "create", "delete", "dislike", "favorite",
|
||||||
|
"follow", "like", "post", "share", "unfavorite", "unfollow",
|
||||||
|
"unlike", "unshare", "update", "tag"]
|
||||||
|
|
||||||
|
def get_url(self, request):
|
||||||
|
return request.urlgen(
|
||||||
|
"mediagoblin.federation.activity_view",
|
||||||
|
username=self.get_actor.username,
|
||||||
|
id=self.id,
|
||||||
|
qualified=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def generate_content(self):
|
||||||
|
""" Produces a HTML content for object """
|
||||||
|
# some of these have simple and targetted. If self.target it set
|
||||||
|
# it will pick the targetted. If they DON'T have a targetted version
|
||||||
|
# the information in targetted won't be added to the content.
|
||||||
|
verb_to_content = {
|
||||||
|
"add": {
|
||||||
|
"simple" : _("{username} added {object}"),
|
||||||
|
"targetted": _("{username} added {object} to {target}"),
|
||||||
|
},
|
||||||
|
"author": {"simple": _("{username} authored {object}")},
|
||||||
|
"create": {"simple": _("{username} created {object}")},
|
||||||
|
"delete": {"simple": _("{username} deleted {object}")},
|
||||||
|
"dislike": {"simple": _("{username} disliked {object}")},
|
||||||
|
"favorite": {"simple": _("{username} favorited {object}")},
|
||||||
|
"follow": {"simple": _("{username} followed {object}")},
|
||||||
|
"like": {"simple": _("{username} liked {object}")},
|
||||||
|
"post": {
|
||||||
|
"simple": _("{username} posted {object}"),
|
||||||
|
"targetted": _("{username} posted {object} to {targetted}"),
|
||||||
|
},
|
||||||
|
"share": {"simple": _("{username} shared {object}")},
|
||||||
|
"unfavorite": {"simple": _("{username} unfavorited {object}")},
|
||||||
|
"unfollow": {"simple": _("{username} stopped following {object}")},
|
||||||
|
"unlike": {"simple": _("{username} unliked {object}")},
|
||||||
|
"unshare": {"simple": _("{username} unshared {object}")},
|
||||||
|
"update": {"simple": _("{username} updated {object}")},
|
||||||
|
"tag": {"simple": _("{username} tagged {object}")},
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = self.get_object()
|
||||||
|
target = self.get_target()
|
||||||
|
actor = self.get_actor
|
||||||
|
content = verb_to_content.get(self.verb, None)
|
||||||
|
|
||||||
|
if content is None or obj is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if target is None or "targetted" not in content:
|
||||||
|
self.content = content["simple"].format(
|
||||||
|
username=actor.username,
|
||||||
|
object=obj.objectType
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.content = content["targetted"].format(
|
||||||
|
username=actor.username,
|
||||||
|
object=obj.objectType,
|
||||||
|
target=target.objectType,
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.content
|
||||||
|
|
||||||
|
def serialize(self, request):
|
||||||
|
obj = {
|
||||||
|
"id": self.id,
|
||||||
|
"actor": self.get_actor.serialize(request),
|
||||||
|
"verb": self.verb,
|
||||||
|
"published": self.published.isoformat(),
|
||||||
|
"updated": self.updated.isoformat(),
|
||||||
|
"content": self.content,
|
||||||
|
"url": self.get_url(request),
|
||||||
|
"object": self.get_object().serialize(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.generator:
|
||||||
|
obj["generator"] = self.get_generator.seralize(request)
|
||||||
|
|
||||||
|
if self.title:
|
||||||
|
obj["title"] = self.title
|
||||||
|
|
||||||
|
target = self.get_target()
|
||||||
|
if target is not None:
|
||||||
|
obj["target"] = target.seralize(request)
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def unseralize(self, data):
|
||||||
|
"""
|
||||||
|
Takes data given and set it on this activity.
|
||||||
|
|
||||||
|
Several pieces of data are not written on because of security
|
||||||
|
reasons. For example changing the author or id of an activity.
|
||||||
|
"""
|
||||||
|
if "verb" in data:
|
||||||
|
self.verb = data["verb"]
|
||||||
|
|
||||||
|
if "title" in data:
|
||||||
|
self.title = data["title"]
|
||||||
|
|
||||||
|
if "content" in data:
|
||||||
|
self.content = data["content"]
|
||||||
|
@ -35,9 +35,9 @@ from mediagoblin.db.extratypes import (PathTupleWithSlashes, JSONEncoded,
|
|||||||
MutationDict)
|
MutationDict)
|
||||||
from mediagoblin.db.base import Base, DictReadAttrProxy
|
from mediagoblin.db.base import Base, DictReadAttrProxy
|
||||||
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, \
|
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, \
|
||||||
MediaCommentMixin, CollectionMixin, CollectionItemMixin
|
MediaCommentMixin, CollectionMixin, CollectionItemMixin, \
|
||||||
|
ActivityMixin
|
||||||
from mediagoblin.tools.files import delete_media_files
|
from mediagoblin.tools.files import delete_media_files
|
||||||
from mediagoblin.tools.translate import pass_to_ugettext as _
|
|
||||||
from mediagoblin.tools.common import import_component
|
from mediagoblin.tools.common import import_component
|
||||||
|
|
||||||
# It's actually kind of annoying how sqlalchemy-migrate does this, if
|
# It's actually kind of annoying how sqlalchemy-migrate does this, if
|
||||||
@ -77,6 +77,11 @@ class User(Base, UserMixin):
|
|||||||
uploaded = Column(Integer, default=0)
|
uploaded = Column(Integer, default=0)
|
||||||
upload_limit = Column(Integer)
|
upload_limit = Column(Integer)
|
||||||
|
|
||||||
|
activity_as_object = Column(Integer,
|
||||||
|
ForeignKey("core__acitivity_intermediators.id"))
|
||||||
|
activity_as_target = Column(Integer,
|
||||||
|
ForeignKey("core__acitivity_intermediators.id"))
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
# plugin data would be in a separate model
|
# plugin data would be in a separate model
|
||||||
|
|
||||||
@ -313,6 +318,11 @@ class MediaEntry(Base, MediaEntryMixin):
|
|||||||
media_metadata = Column(MutationDict.as_mutable(JSONEncoded),
|
media_metadata = Column(MutationDict.as_mutable(JSONEncoded),
|
||||||
default=MutationDict())
|
default=MutationDict())
|
||||||
|
|
||||||
|
activity_as_object = Column(Integer,
|
||||||
|
ForeignKey("core__acitivity_intermediators.id"))
|
||||||
|
activity_as_target = Column(Integer,
|
||||||
|
ForeignKey("core__acitivity_intermediators.id"))
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
# fail_error
|
# fail_error
|
||||||
|
|
||||||
@ -656,6 +666,12 @@ class MediaComment(Base, MediaCommentMixin):
|
|||||||
lazy="dynamic",
|
lazy="dynamic",
|
||||||
cascade="all, delete-orphan"))
|
cascade="all, delete-orphan"))
|
||||||
|
|
||||||
|
|
||||||
|
activity_as_object = Column(Integer,
|
||||||
|
ForeignKey("core__acitivity_intermediators.id"))
|
||||||
|
activity_as_target = Column(Integer,
|
||||||
|
ForeignKey("core__acitivity_intermediators.id"))
|
||||||
|
|
||||||
objectType = "comment"
|
objectType = "comment"
|
||||||
|
|
||||||
def serialize(self, request):
|
def serialize(self, request):
|
||||||
@ -722,6 +738,11 @@ class Collection(Base, CollectionMixin):
|
|||||||
backref=backref("collections",
|
backref=backref("collections",
|
||||||
cascade="all, delete-orphan"))
|
cascade="all, delete-orphan"))
|
||||||
|
|
||||||
|
activity_as_object = Column(Integer,
|
||||||
|
ForeignKey("core__acitivity_intermediators.id"))
|
||||||
|
activity_as_target = Column(Integer,
|
||||||
|
ForeignKey("core__acitivity_intermediators.id"))
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
UniqueConstraint('creator', 'slug'),
|
UniqueConstraint('creator', 'slug'),
|
||||||
{})
|
{})
|
||||||
@ -1072,8 +1093,8 @@ class Generator(Base):
|
|||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
name = Column(Unicode, nullable=False)
|
name = Column(Unicode, nullable=False)
|
||||||
published = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
published = Column(DateTime, default=datetime.datetime.now)
|
||||||
updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
updated = Column(DateTime, default=datetime.datetime.now)
|
||||||
object_type = Column(Unicode, nullable=False)
|
object_type = Column(Unicode, nullable=False)
|
||||||
|
|
||||||
def serialize(self, request):
|
def serialize(self, request):
|
||||||
@ -1090,8 +1111,76 @@ class Generator(Base):
|
|||||||
self.name = data["displayName"]
|
self.name = data["displayName"]
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityIntermediator(Base):
|
||||||
|
"""
|
||||||
|
This is used so that objects/targets can have a foreign key back to this
|
||||||
|
object and activities can a foreign key to this object. This objects to be
|
||||||
|
used multiple times for the activity object or target and also allows for
|
||||||
|
different types of objects to be used as an Activity.
|
||||||
|
"""
|
||||||
|
__tablename__ = "core__acitivity_intermediators"
|
||||||
|
|
||||||
class Activity(Base):
|
id = Column(Integer, primary_key=True)
|
||||||
|
type = Column(Integer, nullable=False)
|
||||||
|
|
||||||
|
TYPES = {
|
||||||
|
0: User,
|
||||||
|
1: MediaEntry,
|
||||||
|
2: MediaComment,
|
||||||
|
3: Collection,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _find_model(self, obj):
|
||||||
|
""" Finds the model for a given object """
|
||||||
|
for key, model in self.TYPES.items():
|
||||||
|
if isinstance(obj, model):
|
||||||
|
return key, model
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def set_object(self, obj):
|
||||||
|
""" This sets itself as the object for an activity """
|
||||||
|
key, model = self._find_model(obj)
|
||||||
|
if key is None:
|
||||||
|
raise ValueError("Invalid type of object given")
|
||||||
|
|
||||||
|
# First set self as activity
|
||||||
|
obj.activity_as_object = self.id
|
||||||
|
self.type = key
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_object(self):
|
||||||
|
""" Finds the object for an activity """
|
||||||
|
if self.type is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
model = self.TYPES[self.type]
|
||||||
|
return model.query.filter_by(activity_as_object=self.id).first()
|
||||||
|
|
||||||
|
def set_target(self, obj):
|
||||||
|
""" This sets itself as the target for an activity """
|
||||||
|
key, model = self._find_model(obj)
|
||||||
|
if key is None:
|
||||||
|
raise ValueError("Invalid type of object given")
|
||||||
|
|
||||||
|
obj.activity_as_target = self.id
|
||||||
|
self.type = key
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_target(self):
|
||||||
|
""" Gets the target for an activity """
|
||||||
|
if self.type is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
model = self.TYPES[self.type]
|
||||||
|
return model.query.filter_by(activity_as_target=self.id).first()
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if self.type not in self.TYPES.keys():
|
||||||
|
raise ValueError("Invalid type set")
|
||||||
|
Base.save(self, *args, **kwargs)
|
||||||
|
|
||||||
|
class Activity(Base, ActivityMixin):
|
||||||
"""
|
"""
|
||||||
This holds all the metadata about an activity such as uploading an image,
|
This holds all the metadata about an activity such as uploading an image,
|
||||||
posting a comment, etc.
|
posting a comment, etc.
|
||||||
@ -1099,182 +1188,59 @@ class Activity(Base):
|
|||||||
__tablename__ = "core__activities"
|
__tablename__ = "core__activities"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
actor = Column(Integer, ForeignKey(User.id), nullable=False)
|
actor = Column(Integer,
|
||||||
|
ForeignKey(User.id, use_alter=True, name="actor"),
|
||||||
|
nullable=False)
|
||||||
published = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
published = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
updated = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
verb = Column(Unicode, nullable=False)
|
verb = Column(Unicode, nullable=False)
|
||||||
content = Column(Unicode, nullable=True)
|
content = Column(Unicode, nullable=True)
|
||||||
title = Column(Unicode, nullable=True)
|
title = Column(Unicode, nullable=True)
|
||||||
target = Column(Integer, ForeignKey(User.id), nullable=True)
|
|
||||||
generator = Column(Integer, ForeignKey(Generator.id), nullable=True)
|
generator = Column(Integer, ForeignKey(Generator.id), nullable=True)
|
||||||
|
object = Column(Integer,
|
||||||
|
ForeignKey(ActivityIntermediator.id), nullable=False)
|
||||||
|
target = Column(Integer,
|
||||||
|
ForeignKey(ActivityIntermediator.id), nullable=True)
|
||||||
|
|
||||||
|
get_actor = relationship(User,
|
||||||
# Links to other models (only one of these should have a value).
|
foreign_keys="Activity.actor", post_update=True)
|
||||||
object_comment = Column(Integer, ForeignKey(MediaComment.id), nullable=True)
|
|
||||||
object_collection = Column(Integer, ForeignKey(Collection.id), nullable=True)
|
|
||||||
object_media = Column(Integer, ForeignKey(MediaEntry.id), nullable=True)
|
|
||||||
object_user = Column(Integer, ForeignKey(User.id), nullable=True)
|
|
||||||
|
|
||||||
# The target could also be several things
|
|
||||||
target_comment = Column(Integer, ForeignKey(MediaComment.id), nullable=True)
|
|
||||||
target_collection = Column(Integer, ForeignKey(Collection.id), nullable=True)
|
|
||||||
target_media = Column(Integer, ForeignKey(MediaEntry.id), nullable=True)
|
|
||||||
target_user = Column(Integer, ForeignKey(User.id), nullable=True)
|
|
||||||
|
|
||||||
get_actor = relationship(User, foreign_keys="Activity.actor")
|
|
||||||
get_generator = relationship(Generator)
|
get_generator = relationship(Generator)
|
||||||
|
|
||||||
VALID_VERBS = ["add", "author", "create", "delete", "dislike", "favorite",
|
def set_object(self, *args, **kwargs):
|
||||||
"follow", "like", "post", "share", "unfavorite", "unfollow",
|
if self.object is None:
|
||||||
"unlike", "unshare", "update", "tag"]
|
ai = ActivityIntermediator()
|
||||||
|
ai.set_object(*args, **kwargs)
|
||||||
def get_object(self):
|
ai.save()
|
||||||
""" This represents the object that is given to the activity """
|
self.object = ai.id
|
||||||
# Do we have a cached version
|
|
||||||
if getattr(self, "_cached_object", None) is not None:
|
|
||||||
return self._cached_object
|
|
||||||
|
|
||||||
if self.object_comment is not None:
|
|
||||||
obj = MediaComment.query.filter_by(id=self.object_comment).first()
|
|
||||||
elif self.object_collection is not None:
|
|
||||||
obj = Collection.query.filter_by(id=self.object_collection).first()
|
|
||||||
elif self.object_media is not None:
|
|
||||||
obj = MediaEntry.query.filter_by(id=self.object_media).first()
|
|
||||||
elif self.object_user is not None:
|
|
||||||
obj = User.query.filter_by(id=self.object_user).first()
|
|
||||||
else:
|
|
||||||
# Shouldn't happen but in case it does
|
|
||||||
return None
|
|
||||||
|
|
||||||
self._cached_object = obj
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def get_target(self):
|
|
||||||
""" This represents the target given on the activity (if any) """
|
|
||||||
if getattr(self, "_cached_target", None) is not None:
|
|
||||||
return self._cached_target
|
|
||||||
|
|
||||||
if self.target_comment is not None:
|
|
||||||
target = MediaComment.query.filter_by(id=self.target_comment).first()
|
|
||||||
elif self.target_collection is not None:
|
|
||||||
target = Collection.query.filter_by(id=self.target_collection).first()
|
|
||||||
elif self.target_media is not None:
|
|
||||||
target = MediaEntry.query.filter_by(id=self.target_media).first()
|
|
||||||
elif self.target_user is not None:
|
|
||||||
target = User.query.filter_by(id=self.target_user).first()
|
|
||||||
else:
|
|
||||||
# Shouldn't happen but in case it does
|
|
||||||
return None
|
|
||||||
|
|
||||||
self._cached_target = target
|
|
||||||
return self._cached_target
|
|
||||||
|
|
||||||
|
|
||||||
def url(self, request):
|
|
||||||
actor = User.query.filter_by(id=self.actor).first()
|
|
||||||
return request.urlgen(
|
|
||||||
"mediagoblin.federation.activity_view",
|
|
||||||
username=actor.username,
|
|
||||||
id=self.id,
|
|
||||||
qualified=True
|
|
||||||
)
|
|
||||||
|
|
||||||
def generate_content(self):
|
|
||||||
"""
|
|
||||||
Produces a HTML content for object
|
|
||||||
TODO: Can this be moved to a mixin?
|
|
||||||
"""
|
|
||||||
# some of these have simple and targetted. If self.target it set
|
|
||||||
# it will pick the targetted. If they DON'T have a targetted version
|
|
||||||
# the information in targetted won't be added to the content.
|
|
||||||
verb_to_content = {
|
|
||||||
"add": {
|
|
||||||
"simple" : _("{username} added {object}"),
|
|
||||||
"targetted": _("{username} added {object} to {target}"),
|
|
||||||
},
|
|
||||||
"author": {"simple": _("{username} authored {object}")},
|
|
||||||
"create": {"simple": _("{username} created {object}")},
|
|
||||||
"delete": {"simple": _("{username} deleted {object}")},
|
|
||||||
"dislike": {"simple": _("{username} disliked {object}")},
|
|
||||||
"favorite": {"simple": _("{username} favorited {object}")},
|
|
||||||
"follow": {"simple": _("{username} followed {object}")},
|
|
||||||
"like": {"simple": _("{username} liked {object}")},
|
|
||||||
"post": {
|
|
||||||
"simple": _("{username} posted {object}"),
|
|
||||||
"targetted": _("{username} posted {object} to {targetted}"),
|
|
||||||
},
|
|
||||||
"share": {"simple": _("{username} shared {object}")},
|
|
||||||
"unfavorite": {"simple": _("{username} unfavorited {object}")},
|
|
||||||
"unfollow": {"simple": _("{username} stopped following {object}")},
|
|
||||||
"unlike": {"simple": _("{username} unliked {object}")},
|
|
||||||
"unshare": {"simple": _("{username} unshared {object}")},
|
|
||||||
"update": {"simple": _("{username} updated {object}")},
|
|
||||||
"tag": {"simple": _("{username} tagged {object}")},
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = self.get_object()
|
|
||||||
target = self.get_target()
|
|
||||||
actor = self.get_actor
|
|
||||||
content = verb_to_content.get(self.verb, None)
|
|
||||||
|
|
||||||
if content is None or obj is None:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if target is None or "targetted" not in content:
|
self.object.set_object(*args, **kwargs)
|
||||||
self.content = content["simple"].format(
|
self.object.save()
|
||||||
username=actor.username,
|
|
||||||
object=obj.objectType
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.content = content["targetted"].format(
|
|
||||||
username=actor.username,
|
|
||||||
object=obj.objectType,
|
|
||||||
target=target.objectType,
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.content
|
@property
|
||||||
|
def get_object(self):
|
||||||
|
return self.object.get_object
|
||||||
|
|
||||||
def serialize(self, request):
|
def set_target(self, *args, **kwargs):
|
||||||
obj = {
|
if self.target is None:
|
||||||
"id": self.id,
|
ai = ActivityIntermediator()
|
||||||
"actor": self.get_actor.serialize(request),
|
ai.set_target(*args, **kwargs)
|
||||||
"verb": self.verb,
|
ai.save()
|
||||||
"published": self.published.isoformat(),
|
self.object = ai.id
|
||||||
"updated": self.updated.isoformat(),
|
return
|
||||||
"content": self.content,
|
|
||||||
"url": self.url(request),
|
|
||||||
"object": self.get_object().serialize(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.generator:
|
self.target.set_object(*args, **kwargs)
|
||||||
obj["generator"] = generator.seralize(request)
|
self.targt.save()
|
||||||
|
|
||||||
if self.title:
|
@property
|
||||||
obj["title"] = self.title
|
def get_target(self):
|
||||||
|
if self.target is None:
|
||||||
|
return None
|
||||||
|
|
||||||
target = self.get_target()
|
return self.target.get_target
|
||||||
if target is not None:
|
|
||||||
obj["target"] = target.seralize(request)
|
|
||||||
|
|
||||||
return obj
|
def save(self, set_updated=True, *args, **kwargs):
|
||||||
|
if set_updated:
|
||||||
def unseralize(self, data):
|
|
||||||
"""
|
|
||||||
Takes data given and set it on this activity.
|
|
||||||
|
|
||||||
Several pieces of data are not written on because of security
|
|
||||||
reasons. For example changing the author or id of an activity.
|
|
||||||
"""
|
|
||||||
if "verb" in data:
|
|
||||||
self.verb = data["verb"]
|
|
||||||
|
|
||||||
if "title" in data:
|
|
||||||
self.title = data["title"]
|
|
||||||
|
|
||||||
if "content" in data:
|
|
||||||
self.content = data["content"]
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
self.updated = datetime.datetime.now()
|
self.updated = datetime.datetime.now()
|
||||||
super(Activity, self).save(*args, **kwargs)
|
super(Activity, self).save(*args, **kwargs)
|
||||||
|
|
||||||
@ -1285,7 +1251,7 @@ MODELS = [
|
|||||||
CommentSubscription, ReportBase, CommentReport, MediaReport, UserBan,
|
CommentSubscription, ReportBase, CommentReport, MediaReport, UserBan,
|
||||||
Privilege, PrivilegeUserAssociation,
|
Privilege, PrivilegeUserAssociation,
|
||||||
RequestToken, AccessToken, NonceTimestamp,
|
RequestToken, AccessToken, NonceTimestamp,
|
||||||
Activity, Generator]
|
Activity, ActivityIntermediator, Generator]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Foundations are the default rows that are created immediately after the tables
|
Foundations are the default rows that are created immediately after the tables
|
||||||
|
Loading…
x
Reference in New Issue
Block a user