aboutsummaryrefslogtreecommitdiffstats
path: root/mediagoblin/db/sql
diff options
context:
space:
mode:
Diffstat (limited to 'mediagoblin/db/sql')
-rw-r--r--mediagoblin/db/sql/__init__.py15
-rw-r--r--mediagoblin/db/sql/base.py107
-rw-r--r--mediagoblin/db/sql/convert.py282
-rw-r--r--mediagoblin/db/sql/extratypes.py63
-rw-r--r--mediagoblin/db/sql/fake.py45
-rw-r--r--mediagoblin/db/sql/migrations.py130
-rw-r--r--mediagoblin/db/sql/models.py469
-rw-r--r--mediagoblin/db/sql/models_v0.py320
-rw-r--r--mediagoblin/db/sql/open.py78
-rw-r--r--mediagoblin/db/sql/util.py327
10 files changed, 0 insertions, 1836 deletions
diff --git a/mediagoblin/db/sql/__init__.py b/mediagoblin/db/sql/__init__.py
deleted file mode 100644
index 621845ba..00000000
--- a/mediagoblin/db/sql/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
diff --git a/mediagoblin/db/sql/base.py b/mediagoblin/db/sql/base.py
deleted file mode 100644
index e10e7739..00000000
--- a/mediagoblin/db/sql/base.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import scoped_session, sessionmaker, object_session
-from sqlalchemy.orm.query import Query
-from sqlalchemy.sql.expression import desc
-from mediagoblin.db.sql.fake import DESCENDING
-
-
-def _get_query_model(query):
- cols = query.column_descriptions
- assert len(cols) == 1, "These functions work only on simple queries"
- return cols[0]["type"]
-
-
-class GMGQuery(Query):
- def sort(self, key, direction):
- key_col = getattr(_get_query_model(self), key)
- if direction is DESCENDING:
- key_col = desc(key_col)
- return self.order_by(key_col)
-
- def skip(self, amount):
- return self.offset(amount)
-
-
-Session = scoped_session(sessionmaker(query_cls=GMGQuery))
-
-
-def _fix_query_dict(query_dict):
- if '_id' in query_dict:
- query_dict['id'] = query_dict.pop('_id')
-
-
-class GMGTableBase(object):
- query = Session.query_property()
-
- @classmethod
- def find(cls, query_dict={}):
- _fix_query_dict(query_dict)
- return cls.query.filter_by(**query_dict)
-
- @classmethod
- def find_one(cls, query_dict={}):
- _fix_query_dict(query_dict)
- return cls.query.filter_by(**query_dict).first()
-
- @classmethod
- def one(cls, query_dict):
- return cls.find(query_dict).one()
-
- def get(self, key):
- return getattr(self, key)
-
- def setdefault(self, key, defaultvalue):
- # The key *has* to exist on sql.
- return getattr(self, key)
-
- def save(self, validate=True):
- assert validate
- sess = object_session(self)
- if sess is None:
- sess = Session()
- sess.add(self)
- sess.commit()
-
- def delete(self, commit=True):
- """Delete the object and commit the change immediately by default"""
- sess = object_session(self)
- assert sess is not None, "Not going to delete detached %r" % self
- sess.delete(self)
- if commit:
- sess.commit()
-
-
-Base = declarative_base(cls=GMGTableBase)
-
-
-class DictReadAttrProxy(object):
- """
- Maps read accesses to obj['key'] to obj.key
- and hides all the rest of the obj
- """
- def __init__(self, proxied_obj):
- self.proxied_obj = proxied_obj
-
- def __getitem__(self, key):
- try:
- return getattr(self.proxied_obj, key)
- except AttributeError:
- raise KeyError("%r is not an attribute on %r"
- % (key, self.proxied_obj))
diff --git a/mediagoblin/db/sql/convert.py b/mediagoblin/db/sql/convert.py
deleted file mode 100644
index ac64cf8d..00000000
--- a/mediagoblin/db/sql/convert.py
+++ /dev/null
@@ -1,282 +0,0 @@
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from copy import copy
-from itertools import chain, imap
-
-from mediagoblin.init import setup_global_and_app_config
-
-from mediagoblin.db.sql.base import Session
-from mediagoblin.db.sql.models_v0 import Base_v0
-from mediagoblin.db.sql.models_v0 import (User, MediaEntry, MediaComment,
- Tag, MediaTag, MediaFile, MediaAttachmentFile, MigrationData,
- ImageData, VideoData, AsciiData, AudioData)
-from mediagoblin.db.sql.open import setup_connection_and_db_from_config as \
- sql_connect
-from mediagoblin.db.mongo.open import setup_connection_and_db_from_config as \
- mongo_connect
-
-
-obj_id_table = dict()
-
-
-def add_obj_ids(entry, new_entry):
- global obj_id_table
- print "\t%r -> SQL id %r" % (entry._id, new_entry.id)
- obj_id_table[entry._id] = new_entry.id
-
-
-def copy_attrs(entry, new_entry, attr_list):
- for a in attr_list:
- val = entry[a]
- setattr(new_entry, a, val)
-
-
-def copy_reference_attr(entry, new_entry, ref_attr):
- val = entry[ref_attr]
- val = obj_id_table[val]
- setattr(new_entry, ref_attr, val)
-
-
-def convert_users(mk_db):
- session = Session()
-
- for entry in mk_db.User.find().sort('created'):
- print entry.username
-
- new_entry = User()
- copy_attrs(entry, new_entry,
- ('username', 'email', 'created', 'pw_hash', 'email_verified',
- 'status', 'verification_key', 'is_admin', 'url',
- 'bio',
- 'fp_verification_key', 'fp_token_expire',))
- # new_entry.fp_verification_expire = entry.fp_token_expire
-
- session.add(new_entry)
- session.flush()
- add_obj_ids(entry, new_entry)
-
- session.commit()
- session.close()
-
-
-def convert_media_entries(mk_db):
- session = Session()
-
- for entry in mk_db.MediaEntry.find().sort('created'):
- print repr(entry.title)
-
- new_entry = MediaEntry()
- copy_attrs(entry, new_entry,
- ('title', 'slug', 'created',
- 'description',
- 'media_type', 'state', 'license',
- 'fail_error', 'fail_metadata',
- 'queued_task_id',))
- copy_reference_attr(entry, new_entry, "uploader")
-
- session.add(new_entry)
- session.flush()
- add_obj_ids(entry, new_entry)
-
- for key, value in entry.media_files.iteritems():
- new_file = MediaFile(name=key, file_path=value)
- new_file.media_entry = new_entry.id
- Session.add(new_file)
-
- for attachment in entry.attachment_files:
- new_attach = MediaAttachmentFile(
- name=attachment["name"],
- filepath=attachment["filepath"],
- created=attachment["created"]
- )
- new_attach.media_entry = new_entry.id
- Session.add(new_attach)
-
- session.commit()
- session.close()
-
-
-def convert_image(mk_db):
- session = Session()
-
- for media in mk_db.MediaEntry.find(
- {'media_type': 'mediagoblin.media_types.image'}).sort('created'):
- media_data = copy(media.media_data)
-
- if len(media_data):
- media_data_row = ImageData(**media_data)
- media_data_row.media_entry = obj_id_table[media['_id']]
- session.add(media_data_row)
-
- session.commit()
- session.close()
-
-
-def convert_video(mk_db):
- session = Session()
-
- for media in mk_db.MediaEntry.find(
- {'media_type': 'mediagoblin.media_types.video'}).sort('created'):
- media_data_row = VideoData(**media.media_data)
- media_data_row.media_entry = obj_id_table[media['_id']]
- session.add(media_data_row)
-
- session.commit()
- session.close()
-
-
-def convert_media_tags(mk_db):
- session = Session()
- session.autoflush = False
-
- for media in mk_db.MediaEntry.find().sort('created'):
- print repr(media.title)
-
- for otag in media.tags:
- print " ", repr((otag["slug"], otag["name"]))
-
- nslug = session.query(Tag).filter_by(slug=otag["slug"]).first()
- print " ", repr(nslug)
- if nslug is None:
- nslug = Tag(slug=otag["slug"])
- session.add(nslug)
- session.flush()
- print " ", repr(nslug), nslug.id
-
- ntag = MediaTag()
- ntag.tag = nslug.id
- ntag.name = otag["name"]
- ntag.media_entry = obj_id_table[media._id]
- session.add(ntag)
-
- session.commit()
- session.close()
-
-
-def convert_media_comments(mk_db):
- session = Session()
-
- for entry in mk_db.MediaComment.find().sort('created'):
- print repr(entry.content)
-
- new_entry = MediaComment()
- copy_attrs(entry, new_entry,
- ('created',
- 'content',))
-
- try:
- copy_reference_attr(entry, new_entry, "media_entry")
- copy_reference_attr(entry, new_entry, "author")
- except KeyError as e:
- print('KeyError in convert_media_comments(): {0}'.format(e))
- else:
- session.add(new_entry)
- session.flush()
- add_obj_ids(entry, new_entry)
-
- session.commit()
- session.close()
-
-
-media_types_tables = (
- ("mediagoblin.media_types.image", (ImageData,)),
- ("mediagoblin.media_types.video", (VideoData,)),
- ("mediagoblin.media_types.ascii", (AsciiData,)),
- ("mediagoblin.media_types.audio", (AudioData,)),
- )
-
-
-def convert_add_migration_versions(dummy_sql_db):
- session = Session()
-
- for name in chain(("__main__",),
- imap(lambda e: e[0], media_types_tables)):
- print "\tAdding %s" % (name,)
- m = MigrationData(name=unicode(name), version=0)
- session.add(m)
-
- session.commit()
- session.close()
-
-
-def cleanup_sql_tables(sql_db):
- for mt, table_list in media_types_tables:
- session = Session()
-
- count = session.query(MediaEntry.media_type). \
- filter_by(media_type=unicode(mt)).count()
- print " %s: %d entries" % (mt, count)
-
- if count == 0:
- print "\tAnalyzing tables"
- for tab in table_list:
- cnt2 = session.query(tab).count()
- print "\t %s: %d entries" % (tab.__tablename__, cnt2)
- assert cnt2 == 0
-
- print "\tRemoving migration info"
- mi = session.query(MigrationData).filter_by(name=unicode(mt)).one()
- session.delete(mi)
- session.commit()
- session.close()
-
- print "\tDropping tables"
- tables = [model.__table__ for model in table_list]
- Base_v0.metadata.drop_all(sql_db.engine, tables=tables)
-
- session.close()
-
-
-def print_header(title):
- print "\n=== %s ===" % (title,)
-
-
-convert_call_list = (
- ("Converting Users", convert_users),
- ("Converting Media Entries", convert_media_entries),
- ("Converting Media Data for Images", convert_image),
- ("Cnnverting Media Data for Videos", convert_video),
- ("Converting Tags for Media", convert_media_tags),
- ("Converting Media Comments", convert_media_comments),
- )
-
-sql_call_list = (
- ("Filling Migration Tables", convert_add_migration_versions),
- ("Analyzing/Cleaning SQL Data", cleanup_sql_tables),
- )
-
-def run_conversion(config_name):
- global_config, app_config = setup_global_and_app_config(config_name)
-
- sql_conn, sql_db = sql_connect(app_config)
- mk_conn, mk_db = mongo_connect(app_config)
-
- Base_v0.metadata.create_all(sql_db.engine)
-
- for title, func in convert_call_list:
- print_header(title)
- func(mk_db)
- Session.remove()
-
- for title, func in sql_call_list:
- print_header(title)
- func(sql_db)
- Session.remove()
-
-
-if __name__ == '__main__':
- run_conversion("mediagoblin.ini")
diff --git a/mediagoblin/db/sql/extratypes.py b/mediagoblin/db/sql/extratypes.py
deleted file mode 100644
index f2304af0..00000000
--- a/mediagoblin/db/sql/extratypes.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-from sqlalchemy.types import TypeDecorator, Unicode, TEXT
-import json
-
-
-class PathTupleWithSlashes(TypeDecorator):
- "Represents a Tuple of strings as a slash separated string."
-
- impl = Unicode
-
- def process_bind_param(self, value, dialect):
- if value is not None:
- if len(value) == 0:
- value = None
- else:
- value = '/'.join(value)
- return value
-
- def process_result_value(self, value, dialect):
- if value is not None:
- value = tuple(value.split('/'))
- return value
-
-
-# The following class and only this one class is in very
-# large parts based on example code from sqlalchemy.
-#
-# The original copyright notice and license follows:
-# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: http://www.opensource.org/licenses/mit-license.php
-#
-class JSONEncoded(TypeDecorator):
- "Represents an immutable structure as a json-encoded string."
-
- impl = TEXT
-
- def process_bind_param(self, value, dialect):
- if value is not None:
- value = json.dumps(value)
- return value
-
- def process_result_value(self, value, dialect):
- if value is not None:
- value = json.loads(value)
- return value
diff --git a/mediagoblin/db/sql/fake.py b/mediagoblin/db/sql/fake.py
deleted file mode 100644
index 0fd0cc41..00000000
--- a/mediagoblin/db/sql/fake.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-This module contains some fake classes and functions to
-calm the rest of the code base. Or provide super minimal
-implementations.
-
-Currently:
-- ObjectId "class": It's a function mostly doing
- int(init_arg) to convert string primary keys into
- integer primary keys.
-- InvalidId exception
-- DESCENDING "constant"
-"""
-
-
-DESCENDING = object() # a unique object for this "constant"
-
-
-class InvalidId(Exception):
- pass
-
-
-def ObjectId(value=None):
- if value is None:
- return None
- try:
- return int(value)
- except ValueError:
- raise InvalidId("%r is an invalid id" % value)
diff --git a/mediagoblin/db/sql/migrations.py b/mediagoblin/db/sql/migrations.py
deleted file mode 100644
index bc68caa3..00000000
--- a/mediagoblin/db/sql/migrations.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import datetime
-
-from sqlalchemy import (MetaData, Table, Column, Boolean, SmallInteger,
- Integer, Unicode, UnicodeText, DateTime,
- ForeignKey, UniqueConstraint)
-from sqlalchemy.ext.declarative import declarative_base
-
-from mediagoblin.db.sql.util import RegisterMigration
-from mediagoblin.db.sql.models import MediaEntry, Collection, User
-
-MIGRATIONS = {}
-
-
-@RegisterMigration(1, MIGRATIONS)
-def ogg_to_webm_audio(db_conn):
- metadata = MetaData(bind=db_conn.bind)
-
- file_keynames = Table('core__file_keynames', metadata, autoload=True,
- autoload_with=db_conn.bind)
-
- db_conn.execute(
- file_keynames.update().where(file_keynames.c.name == 'ogg').
- values(name='webm_audio')
- )
- db_conn.commit()
-
-
-@RegisterMigration(2, MIGRATIONS)
-def add_wants_notification_column(db_conn):
- metadata = MetaData(bind=db_conn.bind)
-
- users = Table('core__users', metadata, autoload=True,
- autoload_with=db_conn.bind)
-
- col = Column('wants_comment_notification', Boolean,
- default=True, nullable=True)
- col.create(users, populate_defaults=True)
- db_conn.commit()
-
-
-@RegisterMigration(3, MIGRATIONS)
-def add_transcoding_progress(db_conn):
- metadata = MetaData(bind=db_conn.bind)
-
- media_entry = Table('core__media_entries', metadata, autoload=True,
- autoload_with=db_conn.bind)
-
- col = Column('transcoding_progress', SmallInteger)
- col.create(media_entry)
- db_conn.commit()
-
-
-class Collection_v0(declarative_base()):
- __tablename__ = "core__collections"
-
- id = Column(Integer, primary_key=True)
- title = Column(Unicode, nullable=False)
- slug = Column(Unicode)
- created = Column(DateTime, nullable=False, default=datetime.datetime.now,
- index=True)
- description = Column(UnicodeText)
- creator = Column(Integer, ForeignKey(User.id), nullable=False)
- items = Column(Integer, default=0)
-
-class CollectionItem_v0(declarative_base()):
- __tablename__ = "core__collection_items"
-
- id = Column(Integer, primary_key=True)
- media_entry = Column(
- Integer, ForeignKey(MediaEntry.id), nullable=False, index=True)
- collection = Column(Integer, ForeignKey(Collection.id), nullable=False)
- note = Column(UnicodeText, nullable=True)
- added = Column(DateTime, nullable=False, default=datetime.datetime.now)
- position = Column(Integer)
-
- ## This should be activated, normally.
- ## But this would change the way the next migration used to work.
- ## So it's commented for now.
- # __table_args__ = (
- # UniqueConstraint('collection', 'media_entry'),
- # {})
-
-@RegisterMigration(4, MIGRATIONS)
-def add_collection_tables(db_conn):
- Collection_v0.__table__.create(db_conn.bind)
- CollectionItem_v0.__table__.create(db_conn.bind)
-
- db_conn.commit()
-
-
-@RegisterMigration(5, MIGRATIONS)
-def add_mediaentry_collected(db_conn):
- metadata = MetaData(bind=db_conn.bind)
-
- media_entry = Table('core__media_entries', metadata, autoload=True,
- autoload_with=db_conn.bind)
-
- col = Column('collected', Integer, default=0)
- col.create(media_entry)
- db_conn.commit()
-
-
-class ProcessingMetaData_v0(declarative_base()):
- __tablename__ = 'core__processing_metadata'
-
- id = Column(Integer, primary_key=True)
- media_entry_id = Column(Integer, ForeignKey(MediaEntry.id), nullable=False,
- index=True)
- callback_url = Column(Unicode)
-
-@RegisterMigration(6, MIGRATIONS)
-def create_processing_metadata_table(db):
- ProcessingMetaData_v0.__table__.create(db.bind)
- db.commit()
diff --git a/mediagoblin/db/sql/models.py b/mediagoblin/db/sql/models.py
deleted file mode 100644
index b48c1fbe..00000000
--- a/mediagoblin/db/sql/models.py
+++ /dev/null
@@ -1,469 +0,0 @@
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-TODO: indexes on foreignkeys, where useful.
-"""
-
-
-import datetime
-import sys
-
-from sqlalchemy import Column, Integer, Unicode, UnicodeText, DateTime, \
- Boolean, ForeignKey, UniqueConstraint, PrimaryKeyConstraint, \
- SmallInteger
-from sqlalchemy.orm import relationship, backref
-from sqlalchemy.orm.collections import attribute_mapped_collection
-from sqlalchemy.sql.expression import desc
-from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy.util import memoized_property
-
-from mediagoblin.db.sql.extratypes import PathTupleWithSlashes, JSONEncoded
-from mediagoblin.db.sql.base import Base, DictReadAttrProxy
-from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, MediaCommentMixin, CollectionMixin, CollectionItemMixin
-from mediagoblin.db.sql.base import Session
-
-# It's actually kind of annoying how sqlalchemy-migrate does this, if
-# I understand it right, but whatever. Anyway, don't remove this :P
-#
-# We could do migration calls more manually instead of relying on
-# this import-based meddling...
-from migrate import changeset
-
-
-class SimpleFieldAlias(object):
- """An alias for any field"""
- def __init__(self, fieldname):
- self.fieldname = fieldname
-
- def __get__(self, instance, cls):
- return getattr(instance, self.fieldname)
-
- def __set__(self, instance, val):
- setattr(instance, self.fieldname, val)
-
-
-class User(Base, UserMixin):
- """
- TODO: We should consider moving some rarely used fields
- into some sort of "shadow" table.
- """
- __tablename__ = "core__users"
-
- id = Column(Integer, primary_key=True)
- username = Column(Unicode, nullable=False, unique=True)
- email = Column(Unicode, nullable=False)
- created = Column(DateTime, nullable=False, default=datetime.datetime.now)
- pw_hash = Column(Unicode, nullable=False)
- email_verified = Column(Boolean, default=False)
- status = Column(Unicode, default=u"needs_email_verification", nullable=False)
- # Intented to be nullable=False, but migrations would not work for it
- # set to nullable=True implicitly.
- wants_comment_notification = Column(Boolean, default=True)
- verification_key = Column(Unicode)
- is_admin = Column(Boolean, default=False, nullable=False)
- url = Column(Unicode)
- bio = Column(UnicodeText) # ??
- fp_verification_key = Column(Unicode)
- fp_token_expire = Column(DateTime)
-
- ## TODO
- # plugin data would be in a separate model
-
- _id = SimpleFieldAlias("id")
-
- def __repr__(self):
- return '<{0} #{1} {2} {3} "{4}">'.format(
- self.__class__.__name__,
- self.id,
- 'verified' if self.email_verified else 'non-verified',
- 'admin' if self.is_admin else 'user',
- self.username)
-
-
-class MediaEntry(Base, MediaEntryMixin):
- """
- TODO: Consider fetching the media_files using join
- """
- __tablename__ = "core__media_entries"
-
- id = Column(Integer, primary_key=True)
- uploader = Column(Integer, ForeignKey(User.id), nullable=False, index=True)
- title = Column(Unicode, nullable=False)
- slug = Column(Unicode)
- created = Column(DateTime, nullable=False, default=datetime.datetime.now,
- index=True)
- description = Column(UnicodeText) # ??
- media_type = Column(Unicode, nullable=False)
- state = Column(Unicode, default=u'unprocessed', nullable=False)
- # or use sqlalchemy.types.Enum?
- license = Column(Unicode)
- collected = Column(Integer, default=0)
-
- fail_error = Column(Unicode)
- fail_metadata = Column(JSONEncoded)
-
- transcoding_progress = Column(SmallInteger)
-
- queued_media_file = Column(PathTupleWithSlashes)
-
- queued_task_id = Column(Unicode)
-
- __table_args__ = (
- UniqueConstraint('uploader', 'slug'),
- {})
-
- get_uploader = relationship(User)
-
- media_files_helper = relationship("MediaFile",
- collection_class=attribute_mapped_collection("name"),
- cascade="all, delete-orphan"
- )
- media_files = association_proxy('media_files_helper', 'file_path',
- creator=lambda k, v: MediaFile(name=k, file_path=v)
- )
-
- attachment_files_helper = relationship("MediaAttachmentFile",
- cascade="all, delete-orphan",
- order_by="MediaAttachmentFile.created"
- )
- attachment_files = association_proxy("attachment_files_helper", "dict_view",
- creator=lambda v: MediaAttachmentFile(
- name=v["name"], filepath=v["filepath"])
- )
-
- tags_helper = relationship("MediaTag",
- cascade="all, delete-orphan"
- )
- tags = association_proxy("tags_helper", "dict_view",
- creator=lambda v: MediaTag(name=v["name"], slug=v["slug"])
- )
-
- collections_helper = relationship("CollectionItem",
- cascade="all, delete-orphan"
- )
- collections = association_proxy("collections_helper", "in_collection")
-
- ## TODO
- # media_data
- # fail_error
-
- _id = SimpleFieldAlias("id")
-
- def get_comments(self, ascending=False):
- order_col = MediaComment.created
- if not ascending:
- order_col = desc(order_col)
- return MediaComment.query.filter_by(
- media_entry=self.id).order_by(order_col)
-
- def url_to_prev(self, urlgen):
- """get the next 'newer' entry by this user"""
- media = MediaEntry.query.filter(
- (MediaEntry.uploader == self.uploader)
- & (MediaEntry.state == u'processed')
- & (MediaEntry.id > self.id)).order_by(MediaEntry.id).first()
-
- if media is not None:
- return media.url_for_self(urlgen)
-
- def url_to_next(self, urlgen):
- """get the next 'older' entry by this user"""
- media = MediaEntry.query.filter(
- (MediaEntry.uploader == self.uploader)
- & (MediaEntry.state == u'processed')
- & (MediaEntry.id < self.id)).order_by(desc(MediaEntry.id)).first()
-
- if media is not None:
- return media.url_for_self(urlgen)
-
- #@memoized_property
- @property
- def media_data(self):
- session = Session()
-
- return session.query(self.media_data_table).filter_by(
- media_entry=self.id).first()
-
- def media_data_init(self, **kwargs):
- """
- Initialize or update the contents of a media entry's media_data row
- """
- session = Session()
-
- media_data = session.query(self.media_data_table).filter_by(
- media_entry=self.id).first()
-
- # No media data, so actually add a new one
- if media_data is None:
- media_data = self.media_data_table(
- media_entry=self.id,
- **kwargs)
- session.add(media_data)
- # Update old media data
- else:
- for field, value in kwargs.iteritems():
- setattr(media_data, field, value)
-
- @memoized_property
- def media_data_table(self):
- # TODO: memoize this
- models_module = self.media_type + '.models'
- __import__(models_module)
- return sys.modules[models_module].DATA_MODEL
-
- def __repr__(self):
- safe_title = self.title.encode('ascii', 'replace')
-
- return '<{classname} {id}: {title}>'.format(
- classname=self.__class__.__name__,
- id=self.id,
- title=safe_title)
-
-
-class FileKeynames(Base):
- """
- keywords for various places.
- currently the MediaFile keys
- """
- __tablename__ = "core__file_keynames"
- id = Column(Integer, primary_key=True)
- name = Column(Unicode, unique=True)
-
- def __repr__(self):
- return "<FileKeyname %r: %r>" % (self.id, self.name)
-
- @classmethod
- def find_or_new(cls, name):
- t = cls.query.filter_by(name=name).first()
- if t is not None:
- return t
- return cls(name=name)
-
-
-class MediaFile(Base):
- """
- TODO: Highly consider moving "name" into a new table.
- TODO: Consider preloading said table in software
- """
- __tablename__ = "core__mediafiles"
-
- media_entry = Column(
- Integer, ForeignKey(MediaEntry.id),
- nullable=False)
- name_id = Column(SmallInteger, ForeignKey(FileKeynames.id), nullable=False)
- file_path = Column(PathTupleWithSlashes)
-
- __table_args__ = (
- PrimaryKeyConstraint('media_entry', 'name_id'),
- {})
-
- def __repr__(self):
- return "<MediaFile %s: %r>" % (self.name, self.file_path)
-
- name_helper = relationship(FileKeynames, lazy="joined", innerjoin=True)
- name = association_proxy('name_helper', 'name',
- creator=FileKeynames.find_or_new
- )
-
-
-class MediaAttachmentFile(Base):
- __tablename__ = "core__attachment_files"
-
- id = Column(Integer, primary_key=True)
- media_entry = Column(
- Integer, ForeignKey(MediaEntry.id),
- nullable=False)
- name = Column(Unicode, nullable=False)
- filepath = Column(PathTupleWithSlashes)
- created = Column(DateTime, nullable=False, default=datetime.datetime.now)
-
- @property
- def dict_view(self):
- """A dict like view on this object"""
- return DictReadAttrProxy(self)
-
-
-class Tag(Base):
- __tablename__ = "core__tags"
-
- id = Column(Integer, primary_key=True)
- slug = Column(Unicode, nullable=False, unique=True)
-
- def __repr__(self):
- return "<Tag %r: %r>" % (self.id, self.slug)
-
- @classmethod
- def find_or_new(cls, slug):
- t = cls.query.filter_by(slug=slug).first()
- if t is not None:
- return t
- return cls(slug=slug)
-
-
-class MediaTag(Base):
- __tablename__ = "core__media_tags"
-
- id = Column(Integer, primary_key=True)
- media_entry = Column(
- Integer, ForeignKey(MediaEntry.id),
- nullable=False, index=True)
- tag = Column(Integer, ForeignKey(Tag.id), nullable=False, index=True)
- name = Column(Unicode)
- # created = Column(DateTime, nullable=False, default=datetime.datetime.now)
-
- __table_args__ = (
- UniqueConstraint('tag', 'media_entry'),
- {})
-
- tag_helper = relationship(Tag)
- slug = association_proxy('tag_helper', 'slug',
- creator=Tag.find_or_new
- )
-
- def __init__(self, name=None, slug=None):
- Base.__init__(self)
- if name is not None:
- self.name = name
- if slug is not None:
- self.tag_helper = Tag.find_or_new(slug)
-
- @property
- def dict_view(self):
- """A dict like view on this object"""
- return DictReadAttrProxy(self)
-
-
-class MediaComment(Base, MediaCommentMixin):
- __tablename__ = "core__media_comments"
-
- id = Column(Integer, primary_key=True)
- media_entry = Column(
- Integer, ForeignKey(MediaEntry.id), nullable=False, index=True)
- author = Column(Integer, ForeignKey(User.id), nullable=False)
- created = Column(DateTime, nullable=False, default=datetime.datetime.now)
- content = Column(UnicodeText, nullable=False)
-
- get_author = relationship(User)
-
- _id = SimpleFieldAlias("id")
-
-
-class Collection(Base, CollectionMixin):
- __tablename__ = "core__collections"
-
- id = Column(Integer, primary_key=True)
- title = Column(Unicode, nullable=False)
- slug = Column(Unicode)
- created = Column(DateTime, nullable=False, default=datetime.datetime.now,
- index=True)
- description = Column(UnicodeText)
- creator = Column(Integer, ForeignKey(User.id), nullable=False)
- items = Column(Integer, default=0)
-
- get_creator = relationship(User)
-
- def get_collection_items(self, ascending=False):
- order_col = CollectionItem.position
- if not ascending:
- order_col = desc(order_col)
- return CollectionItem.query.filter_by(
- collection=self.id).order_by(order_col)
-
- _id = SimpleFieldAlias("id")
-
-
-class CollectionItem(Base, CollectionItemMixin):
- __tablename__ = "core__collection_items"
-
- id = Column(Integer, primary_key=True)
- media_entry = Column(
- Integer, ForeignKey(MediaEntry.id), nullable=False, index=True)
- collection = Column(Integer, ForeignKey(Collection.id), nullable=False)
- note = Column(UnicodeText, nullable=True)
- added = Column(DateTime, nullable=False, default=datetime.datetime.now)
- position = Column(Integer)
- in_collection = relationship("Collection")
-
- get_media_entry = relationship(MediaEntry)
-
- _id = SimpleFieldAlias("id")
-
- __table_args__ = (
- UniqueConstraint('collection', 'media_entry'),
- {})
-
- @property
- def dict_view(self):
- """A dict like view on this object"""
- return DictReadAttrProxy(self)
-
-
-class ProcessingMetaData(Base):
- __tablename__ = 'core__processing_metadata'
-
- id = Column(Integer, primary_key=True)
- media_entry_id = Column(Integer, ForeignKey(MediaEntry.id), nullable=False,
- index=True)
- media_entry = relationship(MediaEntry,
- backref=backref('processing_metadata',
- cascade='all, delete-orphan'))
- callback_url = Column(Unicode)
-
- @property
- def dict_view(self):
- """A dict like view on this object"""
- return DictReadAttrProxy(self)
-
-
-MODELS = [
- User, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem, MediaFile, FileKeynames,
- MediaAttachmentFile, ProcessingMetaData]
-
-
-######################################################
-# Special, migrations-tracking table
-#
-# Not listed in MODELS because this is special and not
-# really migrated, but used for migrations (for now)
-######################################################
-
-class MigrationData(Base):
- __tablename__ = "core__migrations"
-
- name = Column(Unicode, primary_key=True)
- version = Column(Integer, nullable=False, default=0)
-
-######################################################
-
-
-def show_table_init(engine_uri):
- if engine_uri is None:
- engine_uri = 'sqlite:///:memory:'
- from sqlalchemy import create_engine
- engine = create_engine(engine_uri, echo=True)
-
- Base.metadata.create_all(engine)
-
-
-if __name__ == '__main__':
- from sys import argv
- print repr(argv)
- if len(argv) == 2:
- uri = argv[1]
- else:
- uri = None
- show_table_init(uri)
diff --git a/mediagoblin/db/sql/models_v0.py b/mediagoblin/db/sql/models_v0.py
deleted file mode 100644
index 06f87d28..00000000
--- a/mediagoblin/db/sql/models_v0.py
+++ /dev/null
@@ -1,320 +0,0 @@
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-TODO: indexes on foreignkeys, where useful.
-"""
-
-
-import datetime
-import sys
-
-from sqlalchemy import (
- Column, Integer, Unicode, UnicodeText, DateTime, Boolean, ForeignKey,
- UniqueConstraint, PrimaryKeyConstraint, SmallInteger, Float)
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import relationship, backref
-from sqlalchemy.orm.collections import attribute_mapped_collection
-from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy.util import memoized_property
-
-from mediagoblin.db.sql.extratypes import PathTupleWithSlashes, JSONEncoded
-from mediagoblin.db.sql.base import GMGTableBase
-from mediagoblin.db.sql.base import Session
-
-
-Base_v0 = declarative_base(cls=GMGTableBase)
-
-
-class User(Base_v0):
- """
- TODO: We should consider moving some rarely used fields
- into some sort of "shadow" table.
- """
- __tablename__ = "core__users"
-
- id = Column(Integer, primary_key=True)
- username = Column(Unicode, nullable=False, unique=True)
- email = Column(Unicode, nullable=False)
- created = Column(DateTime, nullable=False, default=datetime.datetime.now)
- pw_hash = Column(Unicode, nullable=False)
- email_verified = Column(Boolean, default=False)
- status = Column(Unicode, default=u"needs_email_verification", nullable=False)
- verification_key = Column(Unicode)
- is_admin = Column(Boolean, default=False, nullable=False)
- url = Column(Unicode)
- bio = Column(UnicodeText) # ??
- fp_verification_key = Column(Unicode)
- fp_token_expire = Column(DateTime)
-
- ## TODO
- # plugin data would be in a separate model
-
-
-class MediaEntry(Base_v0):
- """
- TODO: Consider fetching the media_files using join
- """
- __tablename__ = "core__media_entries"
-
- id = Column(Integer, primary_key=True)
- uploader = Column(Integer, ForeignKey(User.id), nullable=False, index=True)
- title = Column(Unicode, nullable=False)
- slug = Column(Unicode)
- created = Column(DateTime, nullable=False, default=datetime.datetime.now,
- index=True)
- description = Column(UnicodeText) # ??
- media_type = Column(Unicode, nullable=False)
- state = Column(Unicode, default=u'unprocessed', nullable=False)
- # or use sqlalchemy.types.Enum?
- license = Column(Unicode)
-
- fail_error = Column(Unicode)
- fail_metadata = Column(JSONEncoded)
-
- queued_media_file = Column(PathTupleWithSlashes)
-
- queued_task_id = Column(Unicode)
-
- __table_args__ = (
- UniqueConstraint('uploader', 'slug'),
- {})
-
- get_uploader = relationship(User)
-
- media_files_helper = relationship("MediaFile",
- collection_class=attribute_mapped_collection("name"),
- cascade="all, delete-orphan"
- )
-
- attachment_files_helper = relationship("MediaAttachmentFile",
- cascade="all, delete-orphan",
- order_by="MediaAttachmentFile.created"
- )
-
- tags_helper = relationship("MediaTag",
- cascade="all, delete-orphan"
- )
-
- def media_data_init(self, **kwargs):
- """
- Initialize or update the contents of a media entry's media_data row
- """
- session = Session()
-
- media_data = session.query(self.media_data_table).filter_by(
- media_entry=self.id).first()
-
- # No media data, so actually add a new one
- if media_data is None:
- media_data = self.media_data_table(
- media_entry=self.id,
- **kwargs)
- session.add(media_data)
- # Update old media data
- else:
- for field, value in kwargs.iteritems():
- setattr(media_data, field, value)
-
- @memoized_property
- def media_data_table(self):
- # TODO: memoize this
- models_module = self.media_type + '.models'
- __import__(models_module)
- return sys.modules[models_module].DATA_MODEL
-
-
-class FileKeynames(Base_v0):
- """
- keywords for various places.
- currently the MediaFile keys
- """
- __tablename__ = "core__file_keynames"
- id = Column(Integer, primary_key=True)
- name = Column(Unicode, unique=True)
-
- def __repr__(self):
- return "<FileKeyname %r: %r>" % (self.id, self.name)
-
- @classmethod
- def find_or_new(cls, name):
- t = cls.query.filter_by(name=name).first()
- if t is not None:
- return t
- return cls(name=name)
-
-
-class MediaFile(Base_v0):
- """
- TODO: Highly consider moving "name" into a new table.
- TODO: Consider preloading said table in software
- """
- __tablename__ = "core__mediafiles"
-
- media_entry = Column(
- Integer, ForeignKey(MediaEntry.id),
- nullable=False)
- name_id = Column(SmallInteger, ForeignKey(FileKeynames.id), nullable=False)
- file_path = Column(PathTupleWithSlashes)
-
- __table_args__ = (
- PrimaryKeyConstraint('media_entry', 'name_id'),
- {})
-
- def __repr__(self):
- return "<MediaFile %s: %r>" % (self.name, self.file_path)
-
- name_helper = relationship(FileKeynames, lazy="joined", innerjoin=True)
- name = association_proxy('name_helper', 'name',
- creator=FileKeynames.find_or_new
- )
-
-
-class MediaAttachmentFile(Base_v0):
- __tablename__ = "core__attachment_files"
-
- id = Column(Integer, primary_key=True)
- media_entry = Column(
- Integer, ForeignKey(MediaEntry.id),
- nullable=False)
- name = Column(Unicode, nullable=False)
- filepath = Column(PathTupleWithSlashes)
- created = Column(DateTime, nullable=False, default=datetime.datetime.now)
-
-
-class Tag(Base_v0):
- __tablename__ = "core__tags"
-
- id = Column(Integer, primary_key=True)
- slug = Column(Unicode, nullable=False, unique=True)
-
- def __repr__(self):
- return "<Tag %r: %r>" % (self.id, self.slug)
-
- @classmethod
- def find_or_new(cls, slug):
- t = cls.query.filter_by(slug=slug).first()
- if t is not None:
- return t
- return cls(slug=slug)
-
-
-class MediaTag(Base_v0):
- __tablename__ = "core__media_tags"
-
- id = Column(Integer, primary_key=True)
- media_entry = Column(
- Integer, ForeignKey(MediaEntry.id),
- nullable=False, index=True)
- tag = Column(Integer, ForeignKey(Tag.id), nullable=False, index=True)
- name = Column(Unicode)
- # created = Column(DateTime, nullable=False, default=datetime.datetime.now)
-
- __table_args__ = (
- UniqueConstraint('tag', 'media_entry'),
- {})
-
- tag_helper = relationship(Tag)
- slug = association_proxy('tag_helper', 'slug',
- creator=Tag.find_or_new
- )
-
- def __init__(self, name=None, slug=None):
- Base_v0.__init__(self)
- if name is not None:
- self.name = name
- if slug is not None:
- self.tag_helper = Tag.find_or_new(slug)
-
-
-class MediaComment(Base_v0):
- __tablename__ = "core__media_comments"
-
- id = Column(Integer, primary_key=True)
- media_entry = Column(
- Integer, ForeignKey(MediaEntry.id), nullable=False, index=True)
- author = Column(Integer, ForeignKey(User.id), nullable=False)
- created = Column(DateTime, nullable=False, default=datetime.datetime.now)
- content = Column(UnicodeText, nullable=False)
-
- get_author = relationship(User)
-
-
-class ImageData(Base_v0):
- __tablename__ = "image__mediadata"
-
- # The primary key *and* reference to the main media_entry
- media_entry = Column(Integer, ForeignKey('core__media_entries.id'),
- primary_key=True)
- get_media_entry = relationship("MediaEntry",
- backref=backref("image__media_data", cascade="all, delete-orphan"))
-
- width = Column(Integer)
- height = Column(Integer)
- exif_all = Column(JSONEncoded)
- gps_longitude = Column(Float)
- gps_latitude = Column(Float)
- gps_altitude = Column(Float)
- gps_direction = Column(Float)
-
-
-class VideoData(Base_v0):
- __tablename__ = "video__mediadata"
-
- # The primary key *and* reference to the main media_entry
- media_entry = Column(Integer, ForeignKey('core__media_entries.id'),
- primary_key=True)
- get_media_entry = relationship("MediaEntry",
- backref=backref("video__media_data", cascade="all, delete-orphan"))
-
- width = Column(SmallInteger)
- height = Column(SmallInteger)
-
-
-class AsciiData(Base_v0):
- __tablename__ = "ascii__mediadata"
-
- # The primary key *and* reference to the main media_entry
- media_entry = Column(Integer, ForeignKey('core__media_entries.id'),
- primary_key=True)
- get_media_entry = relationship("MediaEntry",
- backref=backref("ascii__media_data", cascade="all, delete-orphan"))
-
-
-class AudioData(Base_v0):
- __tablename__ = "audio__mediadata"
-
- # The primary key *and* reference to the main media_entry
- media_entry = Column(Integer, ForeignKey('core__media_entries.id'),
- primary_key=True)
- get_media_entry = relationship("MediaEntry",
- backref=backref("audio__media_data", cascade="all, delete-orphan"))
-
-
-######################################################
-# Special, migrations-tracking table
-#
-# Not listed in MODELS because this is special and not
-# really migrated, but used for migrations (for now)
-######################################################
-
-class MigrationData(Base_v0):
- __tablename__ = "core__migrations"
-
- name = Column(Unicode, primary_key=True)
- version = Column(Integer, nullable=False, default=0)
-
-######################################################
diff --git a/mediagoblin/db/sql/open.py b/mediagoblin/db/sql/open.py
deleted file mode 100644
index 9db21c56..00000000
--- a/mediagoblin/db/sql/open.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-from sqlalchemy import create_engine
-import logging
-
-from mediagoblin.db.sql.base import Base, Session
-from mediagoblin import mg_globals
-
-_log = logging.getLogger(__name__)
-
-
-class DatabaseMaster(object):
- def __init__(self, engine):
- self.engine = engine
-
- for k, v in Base._decl_class_registry.iteritems():
- setattr(self, k, v)
-
- def commit(self):
- Session.commit()
-
- def save(self, obj):
- Session.add(obj)
- Session.flush()
-
- def check_session_clean(self):
- for dummy in Session():
- _log.warn("STRANGE: There are elements in the sql session. "
- "Please report this and help us track this down.")
- break
-
- def reset_after_request(self):
- Session.rollback()
- Session.remove()
-
-
-def load_models(app_config):
- import mediagoblin.db.sql.models
-
- for media_type in app_config['media_types']:
- _log.debug("Loading %s.models", media_type)
- __import__(media_type + ".models")
-
- for plugin in mg_globals.global_config.get('plugins', {}).keys():
- _log.debug("Loading %s.models", plugin)
- try:
- __import__(plugin + ".models")
- except ImportError as exc:
- _log.debug("Could not load {0}.models: {1}".format(
- plugin,
- exc))
-
-
-def setup_connection_and_db_from_config(app_config):
- engine = create_engine(app_config['sql_engine'])
- # logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
- Session.configure(bind=engine)
-
- return "dummy conn", DatabaseMaster(engine)
-
-
-def check_db_migrations_current(db):
- pass
diff --git a/mediagoblin/db/sql/util.py b/mediagoblin/db/sql/util.py
deleted file mode 100644
index c6d8562e..00000000
--- a/mediagoblin/db/sql/util.py
+++ /dev/null
@@ -1,327 +0,0 @@
-# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-import sys
-from mediagoblin.db.sql.base import Session
-from mediagoblin.db.sql.models import MediaEntry, Tag, MediaTag, Collection
-
-from mediagoblin.tools.common import simple_printer
-
-
-class MigrationManager(object):
- """
- Migration handling tool.
-
- Takes information about a database, lets you update the database
- to the latest migrations, etc.
- """
-
- def __init__(self, name, models, migration_registry, session,
- printer=simple_printer):
- """
- Args:
- - name: identifier of this section of the database
- - session: session we're going to migrate
- - migration_registry: where we should find all migrations to
- run
- """
- self.name = unicode(name)
- self.models = models
- self.session = session
- self.migration_registry = migration_registry
- self._sorted_migrations = None
- self.printer = printer
-
- # For convenience
- from mediagoblin.db.sql.models import MigrationData
-
- self.migration_model = MigrationData
- self.migration_table = MigrationData.__table__
-
- @property
- def sorted_migrations(self):
- """
- Sort migrations if necessary and store in self._sorted_migrations
- """
- if not self._sorted_migrations:
- self._sorted_migrations = sorted(
- self.migration_registry.items(),
- # sort on the key... the migration number
- key=lambda migration_tuple: migration_tuple[0])
-
- return self._sorted_migrations
-
- @property
- def migration_data(self):
- """
- Get the migration row associated with this object, if any.
- """
- return self.session.query(
- self.migration_model).filter_by(name=self.name).first()
-
- @property
- def latest_migration(self):
- """
- Return a migration number for the latest migration, or 0 if
- there are no migrations.
- """
- if self.sorted_migrations:
- return self.sorted_migrations[-1][0]
- else:
- # If no migrations have been set, we start at 0.
- return 0
-
- @property
- def database_current_migration(self):
- """
- Return the current migration in the database.
- """
- # If the table doesn't even exist, return None.
- if not self.migration_table.exists(self.session.bind):
- return None
-
- # Also return None if self.migration_data is None.
- if self.migration_data is None:
- return None
-
- return self.migration_data.version
-
- def set_current_migration(self, migration_number=None):
- """
- Set the migration in the database to migration_number
- (or, the latest available)
- """
- self.migration_data.version = migration_number or self.latest_migration
- self.session.commit()
-
- def migrations_to_run(self):
- """
- Get a list of migrations to run still, if any.
-
- Note that this will fail if there's no migration record for
- this class!
- """
- assert self.database_current_migration is not None
-
- db_current_migration = self.database_current_migration
-
- return [
- (migration_number, migration_func)
- for migration_number, migration_func in self.sorted_migrations
- if migration_number > db_current_migration]
-
-
- def init_tables(self):
- """
- Create all tables relative to this package
- """
- # sanity check before we proceed, none of these should be created
- for model in self.models:
- # Maybe in the future just print out a "Yikes!" or something?
- assert not model.__table__.exists(self.session.bind)
-
- self.migration_model.metadata.create_all(
- self.session.bind,
- tables=[model.__table__ for model in self.models])
-
- def create_new_migration_record(self):
- """
- Create a new migration record for this migration set
- """
- migration_record = self.migration_model(
- name=self.name,
- version=self.latest_migration)
- self.session.add(migration_record)
- self.session.commit()
-
- def dry_run(self):
- """
- Print out a dry run of what we would have upgraded.
- """
- if self.database_current_migration is None:
- self.printer(
- u'~> Woulda initialized: %s\n' % self.name_for_printing())
- return u'inited'
-
- migrations_to_run = self.migrations_to_run()
- if migrations_to_run:
- self.printer(
- u'~> Woulda updated %s:\n' % self.name_for_printing())
-
- for migration_number, migration_func in migrations_to_run():
- self.printer(
- u' + Would update %s, "%s"\n' % (
- migration_number, migration_func.func_name))
-
- return u'migrated'
-
- def name_for_printing(self):
- if self.name == u'__main__':
- return u"main mediagoblin tables"
- else:
- # TODO: Use the friendlier media manager "human readable" name
- return u'media type "%s"' % self.name
-
- def init_or_migrate(self):
- """
- Initialize the database or migrate if appropriate.
-
- Returns information about whether or not we initialized
- ('inited'), migrated ('migrated'), or did nothing (None)
- """
- assure_migrations_table_setup(self.session)
-
- # Find out what migration number, if any, this database data is at,
- # and what the latest is.
- migration_number = self.database_current_migration
-
- # Is this our first time? Is there even a table entry for
- # this identifier?
- # If so:
- # - create all tables
- # - create record in migrations registry
- # - print / inform the user
- # - return 'inited'
- if migration_number is None:
- self.printer(u"-> Initializing %s... " % self.name_for_printing())
-
- self.init_tables()
- # auto-set at latest migration number
- self.create_new_migration_record()
-
- self.printer(u"done.\n")
- self.set_current_migration()
- return u'inited'
-
- # Run migrations, if appropriate.
- migrations_to_run = self.migrations_to_run()
- if migrations_to_run:
- self.printer(
- u'-> Updating %s:\n' % self.name_for_printing())
- for migration_number, migration_func in migrations_to_run:
- self.printer(
- u' + Running migration %s, "%s"... ' % (
- migration_number, migration_func.func_name))
- migration_func(self.session)
- self.printer('done.\n')
-
- self.set_current_migration()
- return u'migrated'
-
- # Otherwise return None. Well it would do this anyway, but
- # for clarity... ;)
- return None
-
-
-class RegisterMigration(object):
- """
- Tool for registering migrations
-
- Call like:
-
- @RegisterMigration(33)
- def update_dwarves(database):
- [...]
-
- This will register your migration with the default migration
- registry. Alternately, to specify a very specific
- migration_registry, you can pass in that as the second argument.
-
- Note, the number of your migration should NEVER be 0 or less than
- 0. 0 is the default "no migrations" state!
- """
- def __init__(self, migration_number, migration_registry):
- assert migration_number > 0, "Migration number must be > 0!"
- assert migration_number not in migration_registry, \
- "Duplicate migration numbers detected! That's not allowed!"
-
- self.migration_number = migration_number
- self.migration_registry = migration_registry
-
- def __call__(self, migration):
- self.migration_registry[self.migration_number] = migration
- return migration
-
-
-def assure_migrations_table_setup(db):
- """
- Make sure the migrations table is set up in the database.
- """
- from mediagoblin.db.sql.models import MigrationData
-
- if not MigrationData.__table__.exists(db.bind):
- MigrationData.metadata.create_all(
- db.bind, tables=[MigrationData.__table__])
-
-
-##########################
-# Random utility functions
-##########################
-
-
-def atomic_update(table, query_dict, update_values):
- table.find(query_dict).update(update_values,
- synchronize_session=False)
- Session.commit()
-
-
-def check_media_slug_used(dummy_db, uploader_id, slug, ignore_m_id):
- filt = (MediaEntry.uploader == uploader_id) \
- & (MediaEntry.slug == slug)
- if ignore_m_id is not None:
- filt = filt & (MediaEntry.id != ignore_m_id)
- does_exist = Session.query(MediaEntry.id).filter(filt).first() is not None
- return does_exist
-
-
-def media_entries_for_tag_slug(dummy_db, tag_slug):
- return MediaEntry.query \
- .join(MediaEntry.tags_helper) \
- .join(MediaTag.tag_helper) \
- .filter(
- (MediaEntry.state == u'processed')
- & (Tag.slug == tag_slug))
-
-
-def clean_orphan_tags(commit=True):
- """Search for unused MediaTags and delete them"""
- q1 = Session.query(Tag).outerjoin(MediaTag).filter(MediaTag.id==None)
- for t in q1:
- Session.delete(t)
- # The "let the db do all the work" version:
- # q1 = Session.query(Tag.id).outerjoin(MediaTag).filter(MediaTag.id==None)
- # q2 = Session.query(Tag).filter(Tag.id.in_(q1))
- # q2.delete(synchronize_session = False)
- if commit:
- Session.commit()
-
-
-def check_collection_slug_used(dummy_db, creator_id, slug, ignore_c_id):
- filt = (Collection.creator == creator_id) \
- & (Collection.slug == slug)
- if ignore_c_id is not None:
- filt = filt & (Collection.id != ignore_c_id)
- does_exist = Session.query(Collection.id).filter(filt).first() is not None
- return does_exist
-
-
-if __name__ == '__main__':
- from mediagoblin.db.sql.open import setup_connection_and_db_from_config
-
- conn,db = setup_connection_and_db_from_config({'sql_engine':'sqlite:///mediagoblin.db'})
-
- clean_orphan_tags()