Skip to content
Browse files

initial

  • Loading branch information...
0 parents commit 68f472577233a3553d454273dc42904d8b601636 @ramusus committed Dec 20, 2012
3 .gitignore
@@ -0,0 +1,3 @@
+/dist
+*.pyc
+*.egg-info
27 LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2011, ramusus and contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of Django nor the names of its contributors may be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4 MANIFEST.in
@@ -0,0 +1,4 @@
+include README.md
+include LICENSE
+include MANIFEST.in
+recursive-include vkontakte_wall *
3 README.md
@@ -0,0 +1,3 @@
+#### Вступление
+
+Приложение позволяет взаимодействовать со стенами Вконтакте, сообщениями и комментариями на них через Вконтакте API и парсер используя стандартные модели Django
30 setup.py
@@ -0,0 +1,30 @@
+from setuptools import setup, find_packages
+
+setup(
+ name='django-vkontakte-wall',
+ version=__import__('vkontakte_wall').__version__,
+ description='Django implementation for vkontakte API Wall',
+ long_description=open('README.md').read(),
+ author='ramusus',
+ author_email='ramusus@gmail.com',
+ url='https://github.com/ramusus/django-vkontakte-wall',
+ download_url='http://pypi.python.org/pypi/django-vkontakte-wall',
+ license='BSD',
+ packages=find_packages(),
+ include_package_data=True,
+ zip_safe=False, # because we're including media that Django needs
+ install_requires=[
+ 'django-vkontakte-api==0.1.1',
+ 'factory_boy',
+# 'django-ajax-selects',
+ ],
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ ],
+)
2 vkontakte_wall/__init__.py
@@ -0,0 +1,2 @@
+VERSION = (0, 1, 1)
+__version__ = '.'.join(map(str, VERSION))
26 vkontakte_wall/admin.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+from django.contrib import admin
+from django.utils.translation import ugettext as _
+from vkontakte_api.admin import VkontakteModelAdmin
+from models import Post, Comment
+
+class CommentInline(admin.TabularInline):
+ model = Comment
+ extra = 0
+ can_delete = False
+ fields = ('author','text','date','likes')
+ readonly_fields = fields
+
+class PostAdmin(VkontakteModelAdmin):
+ list_display = ('wall_owner','text','author','vk_link','date','comments','likes','reposts')
+ list_display_links = ('text',)
+# list_filter = ('wall_owner',)
+ search_fields = ('text','copy_text')
+ exclude = ('like_users','repost_users',)
+ inlines = [CommentInline]
+
+class CommentAdmin(VkontakteModelAdmin):
+ list_display = ('author','post','vk_link','date','likes')
+
+admin.site.register(Post, PostAdmin)
+admin.site.register(Comment, CommentAdmin)
28 vkontakte_wall/factories.py
@@ -0,0 +1,28 @@
+from vkontakte_users.factories import UserFactory
+from vkontakte_groups.factories import GroupFactory
+from models import Post, Comment
+from datetime import datetime
+import factory
+import random
+
+class PostFactory(factory.Factory):
+ FACTORY_FOR = Post
+
+ date = datetime.now()
+
+ wall_owner = factory.SubFactory(UserFactory)
+ author = factory.SubFactory(UserFactory)
+ remote_id = factory.LazyAttributeSequence(lambda o, n: '%s_%s' % (o.wall_owner.remote_id, n))
+
+class GroupPostFactory(PostFactory):
+ wall_owner = factory.SubFactory(GroupFactory)
+ remote_id = factory.LazyAttributeSequence(lambda o, n: '-%s_%s' % (o.wall_owner.remote_id, n))
+
+class CommentFactory(factory.Factory):
+ FACTORY_FOR = Comment
+
+ date = datetime.now()
+
+ post = factory.SubFactory(PostFactory)
+ author = factory.SubFactory(UserFactory)
+ remote_id = factory.Sequence(lambda n: n)
159 vkontakte_wall/migrations/0001_initial.py
@@ -0,0 +1,159 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'Post'
+ db.create_table('vkontakte_wall_post', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('fetched', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ('remote_id', self.gf('django.db.models.fields.CharField')(unique=True, max_length='20')),
+ ('owner', self.gf('django.db.models.fields.related.ForeignKey')(related_name='wall', to=orm['vkontakte_users.User'])),
+ ('author', self.gf('django.db.models.fields.related.ForeignKey')(related_name='posts', to=orm['vkontakte_users.User'])),
+ ('date', self.gf('django.db.models.fields.DateTimeField')()),
+ ('text', self.gf('django.db.models.fields.TextField')()),
+ ('comments', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+ ('likes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+ ('reposts', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+ ('attachments', self.gf('django.db.models.fields.TextField')()),
+ ('media', self.gf('django.db.models.fields.TextField')()),
+ ('geo', self.gf('django.db.models.fields.TextField')()),
+ ('signer_id', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
+ ('copy_owner_id', self.gf('django.db.models.fields.IntegerField')(null=True)),
+ ('copy_post_id', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
+ ('copy_text', self.gf('django.db.models.fields.TextField')()),
+ ('post_source', self.gf('django.db.models.fields.TextField')()),
+ ('online', self.gf('django.db.models.fields.PositiveSmallIntegerField')(null=True)),
+ ('reply_count', self.gf('django.db.models.fields.PositiveSmallIntegerField')(null=True)),
+ ))
+ db.send_create_signal('vkontakte_wall', ['Post'])
+
+ # Adding model 'Comment'
+ db.create_table('vkontakte_wall_comment', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('fetched', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ('remote_id', self.gf('django.db.models.fields.CharField')(unique=True, max_length='20')),
+ ('post', self.gf('django.db.models.fields.related.ForeignKey')(related_name='wall_comments', to=orm['vkontakte_wall.Post'])),
+ ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='comments', to=orm['vkontakte_users.User'])),
+ ('from_id', self.gf('django.db.models.fields.IntegerField')(null=True)),
+ ('reply_to_uid', self.gf('django.db.models.fields.IntegerField')(null=True)),
+ ('reply_to_cid', self.gf('django.db.models.fields.IntegerField')(null=True)),
+ ('date', self.gf('django.db.models.fields.DateTimeField')()),
+ ('text', self.gf('django.db.models.fields.TextField')()),
+ ('likes', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
+ ))
+ db.send_create_signal('vkontakte_wall', ['Comment'])
+
+ def backwards(self, orm):
+ # Deleting model 'Post'
+ db.delete_table('vkontakte_wall_post')
+
+ # Deleting model 'Comment'
+ db.delete_table('vkontakte_wall_comment')
+
+ models = {
+ 'vkontakte_places.city': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'City'},
+ 'area': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'country': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cities'", 'null': 'True', 'to': "orm['vkontakte_places.Country']"}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'region': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'})
+ },
+ 'vkontakte_places.country': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Country'},
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'})
+ },
+ 'vkontakte_users.user': {
+ 'Meta': {'ordering': "['remote_id']", 'object_name': 'User'},
+ 'activity': ('django.db.models.fields.TextField', [], {}),
+ 'albums': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'audios': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'bdate': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'city': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_places.City']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'counters_updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_places.Country']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'faculty': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'faculty_name': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ 'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'friends': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'graduation': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'has_mobile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'home_phone': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'mutual_friends': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'notes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'photo': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_big': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_medium': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_medium_rec': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_rec': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'rate': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'relation': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'}),
+ 'screen_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'sex': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'subscriptions': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'sum_counters': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'timezone': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'university': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'university_name': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+ 'user_photos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'user_videos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'videos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'wall_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'vkontakte_wall.comment': {
+ 'Meta': {'ordering': "['remote_id']", 'object_name': 'Comment'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'from_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'likes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wall_comments'", 'to': "orm['vkontakte_wall.Post']"}),
+ 'remote_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': "'20'"}),
+ 'reply_to_cid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'reply_to_uid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['vkontakte_users.User']"})
+ },
+ 'vkontakte_wall.post': {
+ 'Meta': {'ordering': "['remote_id']", 'object_name': 'Post'},
+ 'attachments': ('django.db.models.fields.TextField', [], {}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['vkontakte_users.User']"}),
+ 'comments': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'copy_owner_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'copy_post_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'copy_text': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateTimeField', [], {}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'geo': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'likes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'media': ('django.db.models.fields.TextField', [], {}),
+ 'online': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wall'", 'to': "orm['vkontakte_users.User']"}),
+ 'post_source': ('django.db.models.fields.TextField', [], {}),
+ 'remote_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': "'20'"}),
+ 'reply_count': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'reposts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'signer_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {})
+ }
+ }
+
+ complete_apps = ['vkontakte_wall']
204 ...migrations/0002_auto__del_field_comment_reply_to_uid__del_field_comment_reply_to_cid__.py
@@ -0,0 +1,204 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Deleting field 'Comment.reply_to_uid'
+ db.delete_column('vkontakte_wall_comment', 'reply_to_uid')
+
+ # Deleting field 'Comment.reply_to_cid'
+ db.delete_column('vkontakte_wall_comment', 'reply_to_cid')
+
+ # Adding field 'Comment.reply_for'
+ db.add_column('vkontakte_wall_comment', 'reply_for',
+ self.gf('django.db.models.fields.related.ForeignKey')(to=orm['vkontakte_users.User'], null=True),
+ keep_default=False)
+
+ # Adding field 'Comment.reply_to'
+ db.add_column('vkontakte_wall_comment', 'reply_to',
+ self.gf('django.db.models.fields.related.ForeignKey')(to=orm['vkontakte_wall.Comment'], null=True),
+ keep_default=False)
+
+ # Adding field 'Post.group'
+ db.add_column('vkontakte_wall_post', 'group',
+ self.gf('django.db.models.fields.related.ForeignKey')(related_name='posts', null=True, to=orm['vkontakte_groups.Group']),
+ keep_default=False)
+
+ # Adding M2M table for field like_users on 'Post'
+ db.create_table('vkontakte_wall_post_like_users', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('post', models.ForeignKey(orm['vkontakte_wall.post'], null=False)),
+ ('user', models.ForeignKey(orm['vkontakte_users.user'], null=False))
+ ))
+ db.create_unique('vkontakte_wall_post_like_users', ['post_id', 'user_id'])
+
+ # Adding M2M table for field repost_users on 'Post'
+ db.create_table('vkontakte_wall_post_repost_users', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('post', models.ForeignKey(orm['vkontakte_wall.post'], null=False)),
+ ('user', models.ForeignKey(orm['vkontakte_users.user'], null=False))
+ ))
+ db.create_unique('vkontakte_wall_post_repost_users', ['post_id', 'user_id'])
+
+
+ # Changing field 'Post.author'
+ db.alter_column('vkontakte_wall_post', 'author_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['vkontakte_users.User']))
+
+ # Changing field 'Post.owner'
+ db.alter_column('vkontakte_wall_post', 'owner_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['vkontakte_users.User']))
+ def backwards(self, orm):
+ # Adding field 'Comment.reply_to_uid'
+ db.add_column('vkontakte_wall_comment', 'reply_to_uid',
+ self.gf('django.db.models.fields.IntegerField')(null=True),
+ keep_default=False)
+
+ # Adding field 'Comment.reply_to_cid'
+ db.add_column('vkontakte_wall_comment', 'reply_to_cid',
+ self.gf('django.db.models.fields.IntegerField')(null=True),
+ keep_default=False)
+
+ # Deleting field 'Comment.reply_for'
+ db.delete_column('vkontakte_wall_comment', 'reply_for_id')
+
+ # Deleting field 'Comment.reply_to'
+ db.delete_column('vkontakte_wall_comment', 'reply_to_id')
+
+ # Deleting field 'Post.group'
+ db.delete_column('vkontakte_wall_post', 'group_id')
+
+ # Removing M2M table for field like_users on 'Post'
+ db.delete_table('vkontakte_wall_post_like_users')
+
+ # Removing M2M table for field repost_users on 'Post'
+ db.delete_table('vkontakte_wall_post_repost_users')
+
+
+ # Changing field 'Post.author'
+ db.alter_column('vkontakte_wall_post', 'author_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['vkontakte_users.User']))
+
+ # Changing field 'Post.owner'
+ db.alter_column('vkontakte_wall_post', 'owner_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['vkontakte_users.User']))
+ models = {
+ 'vkontakte_groups.group': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Group'},
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '800'}),
+ 'photo': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_big': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_medium': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'}),
+ 'screen_name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['vkontakte_users.User']", 'symmetrical': 'False'})
+ },
+ 'vkontakte_places.city': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'City'},
+ 'area': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'country': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cities'", 'null': 'True', 'to': "orm['vkontakte_places.Country']"}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'region': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'})
+ },
+ 'vkontakte_places.country': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Country'},
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'})
+ },
+ 'vkontakte_users.user': {
+ 'Meta': {'ordering': "['remote_id']", 'object_name': 'User'},
+ 'activity': ('django.db.models.fields.TextField', [], {}),
+ 'albums': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'audios': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'bdate': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'city': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_places.City']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'counters_updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_places.Country']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'faculty': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'faculty_name': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ 'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'friends': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'graduation': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'has_mobile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'home_phone': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'mutual_friends': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'notes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'photo': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_big': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_medium': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_medium_rec': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_rec': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'rate': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'relation': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'}),
+ 'screen_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'sex': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'subscriptions': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'sum_counters': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'timezone': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'university': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'university_name': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+ 'user_photos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'user_videos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'videos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'wall_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'vkontakte_wall.comment': {
+ 'Meta': {'ordering': "['remote_id']", 'object_name': 'Comment'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'from_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'likes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wall_comments'", 'to': "orm['vkontakte_wall.Post']"}),
+ 'remote_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': "'20'"}),
+ 'reply_for': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_users.User']", 'null': 'True'}),
+ 'reply_to': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_wall.Comment']", 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['vkontakte_users.User']"})
+ },
+ 'vkontakte_wall.post': {
+ 'Meta': {'ordering': "['remote_id']", 'object_name': 'Post'},
+ 'attachments': ('django.db.models.fields.TextField', [], {}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'null': 'True', 'to': "orm['vkontakte_users.User']"}),
+ 'comments': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'copy_owner_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'copy_post_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'copy_text': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateTimeField', [], {}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'geo': ('django.db.models.fields.TextField', [], {}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'null': 'True', 'to': "orm['vkontakte_groups.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'like_users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'like_posts'", 'blank': 'True', 'to': "orm['vkontakte_users.User']"}),
+ 'likes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'media': ('django.db.models.fields.TextField', [], {}),
+ 'online': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wall'", 'null': 'True', 'to': "orm['vkontakte_users.User']"}),
+ 'post_source': ('django.db.models.fields.TextField', [], {}),
+ 'remote_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': "'20'"}),
+ 'reply_count': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'repost_users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'repost_posts'", 'blank': 'True', 'to': "orm['vkontakte_users.User']"}),
+ 'reposts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'signer_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {})
+ }
+ }
+
+ complete_apps = ['vkontakte_wall']
229 ...migrations/0003_auto__del_field_comment_reply_for__del_field_comment_user__add_field_c.py
@@ -0,0 +1,229 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Deleting field 'Comment.reply_for'
+ db.delete_column('vkontakte_wall_comment', 'reply_for_id')
+
+ # Deleting field 'Comment.user'
+ db.delete_column('vkontakte_wall_comment', 'user_id')
+
+ # Adding field 'Comment.author_content_type'
+ db.add_column('vkontakte_wall_comment', 'author_content_type',
+ self.gf('django.db.models.fields.related.ForeignKey')(default=45, related_name='comments', to=orm['contenttypes.ContentType']),
+ keep_default=False)
+
+ # Adding field 'Comment.author_id'
+ db.add_column('vkontakte_wall_comment', 'author_id',
+ self.gf('django.db.models.fields.PositiveIntegerField')(default=0),
+ keep_default=False)
+
+ # Adding field 'Comment.reply_for_content_type'
+ db.add_column('vkontakte_wall_comment', 'reply_for_content_type',
+ self.gf('django.db.models.fields.related.ForeignKey')(related_name='replies', null=True, to=orm['contenttypes.ContentType']),
+ keep_default=False)
+
+ # Adding field 'Comment.reply_for_id'
+ db.add_column('vkontakte_wall_comment', 'reply_for_id',
+ self.gf('django.db.models.fields.PositiveIntegerField')(null=True),
+ keep_default=False)
+
+ # Deleting field 'Post.owner'
+ db.delete_column('vkontakte_wall_post', 'owner_id')
+
+ # Deleting field 'Post.group'
+ db.delete_column('vkontakte_wall_post', 'group_id')
+
+ # Deleting field 'Post.author'
+ db.delete_column('vkontakte_wall_post', 'author_id')
+
+ # Adding field 'Post.wall_owner_content_type'
+ db.add_column('vkontakte_wall_post', 'wall_owner_content_type',
+ self.gf('django.db.models.fields.related.ForeignKey')(default=39, related_name='wall_posts', to=orm['contenttypes.ContentType']),
+ keep_default=False)
+
+ # Adding field 'Post.wall_owner_id'
+ db.add_column('vkontakte_wall_post', 'wall_owner_id',
+ self.gf('django.db.models.fields.PositiveIntegerField')(default=0),
+ keep_default=False)
+
+ # Adding field 'Post.author_content_type'
+ db.add_column('vkontakte_wall_post', 'author_content_type',
+ self.gf('django.db.models.fields.related.ForeignKey')(default=45, related_name='posts', to=orm['contenttypes.ContentType']),
+ keep_default=False)
+
+ # Adding field 'Post.author_id'
+ db.add_column('vkontakte_wall_post', 'author_id',
+ self.gf('django.db.models.fields.PositiveIntegerField')(default=0),
+ keep_default=False)
+
+ def backwards(self, orm):
+ # Adding field 'Comment.reply_for'
+ db.add_column('vkontakte_wall_comment', 'reply_for',
+ self.gf('django.db.models.fields.related.ForeignKey')(to=orm['vkontakte_users.User'], null=True),
+ keep_default=False)
+
+ # Adding field 'Comment.user'
+ db.add_column('vkontakte_wall_comment', 'user',
+ self.gf('django.db.models.fields.related.ForeignKey')(default=0, related_name='comments', to=orm['vkontakte_users.User']),
+ keep_default=False)
+
+ # Deleting field 'Comment.author_content_type'
+ db.delete_column('vkontakte_wall_comment', 'author_content_type_id')
+
+ # Deleting field 'Comment.author_id'
+ db.delete_column('vkontakte_wall_comment', 'author_id')
+
+ # Deleting field 'Comment.reply_for_content_type'
+ db.delete_column('vkontakte_wall_comment', 'reply_for_content_type_id')
+
+ # Deleting field 'Comment.reply_for_id'
+ db.delete_column('vkontakte_wall_comment', 'reply_for_id')
+
+ # Adding field 'Post.owner'
+ db.add_column('vkontakte_wall_post', 'owner',
+ self.gf('django.db.models.fields.related.ForeignKey')(related_name='wall', null=True, to=orm['vkontakte_users.User']),
+ keep_default=False)
+
+ # Adding field 'Post.group'
+ db.add_column('vkontakte_wall_post', 'group',
+ self.gf('django.db.models.fields.related.ForeignKey')(related_name='posts', null=True, to=orm['vkontakte_groups.Group']),
+ keep_default=False)
+
+ # Adding field 'Post.author'
+ db.add_column('vkontakte_wall_post', 'author',
+ self.gf('django.db.models.fields.related.ForeignKey')(related_name='posts', null=True, to=orm['vkontakte_users.User']),
+ keep_default=False)
+
+ # Deleting field 'Post.wall_owner_content_type'
+ db.delete_column('vkontakte_wall_post', 'wall_owner_content_type_id')
+
+ # Deleting field 'Post.wall_owner_id'
+ db.delete_column('vkontakte_wall_post', 'wall_owner_id')
+
+ # Deleting field 'Post.author_content_type'
+ db.delete_column('vkontakte_wall_post', 'author_content_type_id')
+
+ # Deleting field 'Post.author_id'
+ db.delete_column('vkontakte_wall_post', 'author_id')
+
+ models = {
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'vkontakte_places.city': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'City'},
+ 'area': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'country': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cities'", 'null': 'True', 'to': "orm['vkontakte_places.Country']"}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'region': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'})
+ },
+ 'vkontakte_places.country': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Country'},
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'})
+ },
+ 'vkontakte_users.user': {
+ 'Meta': {'ordering': "['remote_id']", 'object_name': 'User'},
+ 'activity': ('django.db.models.fields.TextField', [], {}),
+ 'albums': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'audios': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'bdate': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'city': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_places.City']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'counters_updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_places.Country']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'faculty': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'faculty_name': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ 'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'friends': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'graduation': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'has_mobile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'home_phone': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'mutual_friends': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'notes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'photo': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_big': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_medium': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_medium_rec': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_rec': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'rate': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'relation': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'}),
+ 'screen_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'sex': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'subscriptions': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'sum_counters': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'timezone': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'university': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'university_name': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+ 'user_photos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'user_videos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'videos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'wall_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'vkontakte_wall.comment': {
+ 'Meta': {'ordering': "['post', 'date']", 'object_name': 'Comment'},
+ 'author_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['contenttypes.ContentType']"}),
+ 'author_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'date': ('django.db.models.fields.DateTimeField', [], {}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'from_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'likes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wall_comments'", 'to': "orm['vkontakte_wall.Post']"}),
+ 'remote_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': "'20'"}),
+ 'reply_for_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'replies'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'reply_for_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'reply_to': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_wall.Comment']", 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {})
+ },
+ 'vkontakte_wall.post': {
+ 'Meta': {'ordering': "['wall_owner_id', 'date']", 'object_name': 'Post'},
+ 'attachments': ('django.db.models.fields.TextField', [], {}),
+ 'author_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['contenttypes.ContentType']"}),
+ 'author_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'comments': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'copy_owner_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'copy_post_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'copy_text': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateTimeField', [], {}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'geo': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'like_users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'like_posts'", 'blank': 'True', 'to': "orm['vkontakte_users.User']"}),
+ 'likes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'media': ('django.db.models.fields.TextField', [], {}),
+ 'online': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'post_source': ('django.db.models.fields.TextField', [], {}),
+ 'remote_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': "'20'"}),
+ 'reply_count': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'repost_users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'repost_posts'", 'blank': 'True', 'to': "orm['vkontakte_users.User']"}),
+ 'reposts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'signer_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'wall_owner_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wall_posts'", 'to': "orm['contenttypes.ContentType']"}),
+ 'wall_owner_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ }
+ }
+
+ complete_apps = ['vkontakte_wall']
143 vkontakte_wall/migrations/0004_auto__add_field_comment_raw_html__add_field_post_raw_html.py
@@ -0,0 +1,143 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Comment.raw_html'
+ db.add_column('vkontakte_wall_comment', 'raw_html',
+ self.gf('django.db.models.fields.TextField')(default=''),
+ keep_default=False)
+
+ # Adding field 'Post.raw_html'
+ db.add_column('vkontakte_wall_post', 'raw_html',
+ self.gf('django.db.models.fields.TextField')(default=''),
+ keep_default=False)
+
+ def backwards(self, orm):
+ # Deleting field 'Comment.raw_html'
+ db.delete_column('vkontakte_wall_comment', 'raw_html')
+
+ # Deleting field 'Post.raw_html'
+ db.delete_column('vkontakte_wall_post', 'raw_html')
+
+ models = {
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'vkontakte_places.city': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'City'},
+ 'area': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'country': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cities'", 'null': 'True', 'to': "orm['vkontakte_places.Country']"}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'region': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'})
+ },
+ 'vkontakte_places.country': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Country'},
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'})
+ },
+ 'vkontakte_users.user': {
+ 'Meta': {'ordering': "['remote_id']", 'object_name': 'User'},
+ 'activity': ('django.db.models.fields.TextField', [], {}),
+ 'albums': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'audios': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'bdate': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'city': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_places.City']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'counters_updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_places.Country']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
+ 'faculty': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'faculty_name': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ 'followers': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'friends': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'graduation': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'has_mobile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'home_phone': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'mutual_friends': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'notes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'photo': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_big': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_medium': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_medium_rec': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'photo_rec': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'rate': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'relation': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+ 'remote_id': ('django.db.models.fields.BigIntegerField', [], {'unique': 'True'}),
+ 'screen_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'sex': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'subscriptions': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'sum_counters': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'timezone': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'university': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'university_name': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+ 'user_photos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'user_videos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'videos': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'wall_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'vkontakte_wall.comment': {
+ 'Meta': {'ordering': "['post', '-date']", 'object_name': 'Comment'},
+ 'author_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['contenttypes.ContentType']"}),
+ 'author_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'date': ('django.db.models.fields.DateTimeField', [], {}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'from_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'likes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wall_comments'", 'to': "orm['vkontakte_wall.Post']"}),
+ 'raw_html': ('django.db.models.fields.TextField', [], {}),
+ 'remote_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': "'20'"}),
+ 'reply_for_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'replies'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'reply_for_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'reply_to': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['vkontakte_wall.Comment']", 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {})
+ },
+ 'vkontakte_wall.post': {
+ 'Meta': {'ordering': "['wall_owner_id', '-date']", 'object_name': 'Post'},
+ 'attachments': ('django.db.models.fields.TextField', [], {}),
+ 'author_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['contenttypes.ContentType']"}),
+ 'author_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'comments': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'copy_owner_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'copy_post_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'copy_text': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateTimeField', [], {}),
+ 'fetched': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'geo': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'like_users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'like_posts'", 'blank': 'True', 'to': "orm['vkontakte_users.User']"}),
+ 'likes': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'media': ('django.db.models.fields.TextField', [], {}),
+ 'online': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'post_source': ('django.db.models.fields.TextField', [], {}),
+ 'raw_html': ('django.db.models.fields.TextField', [], {}),
+ 'remote_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': "'20'"}),
+ 'reply_count': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'repost_users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'repost_posts'", 'blank': 'True', 'to': "orm['vkontakte_users.User']"}),
+ 'reposts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'signer_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'wall_owner_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wall_posts'", 'to': "orm['contenttypes.ContentType']"}),
+ 'wall_owner_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ }
+ }
+
+ complete_apps = ['vkontakte_wall']
0 vkontakte_wall/migrations/__init__.py
No changes.
477 vkontakte_wall/models.py
@@ -0,0 +1,477 @@
+# -*- coding: utf-8 -*-
+from django.db import models
+from django.dispatch import Signal
+from django.contrib.contenttypes.models import ContentType
+from django.contrib.contenttypes import generic
+from datetime import datetime
+from vkontakte_api.utils import api_call
+from vkontakte_api import fields
+from vkontakte_api.models import VkontakteManager, VkontakteModel
+from vkontakte_users.models import User
+from vkontakte_groups.models import Group
+from parser import VkontakteWallParser, VkontakteParseError
+import logging
+import re
+
+log = logging.getLogger('vkontakte_wall')
+
+parsed = Signal(providing_args=['sender', 'instance', 'container'])
+
+class VkontaktePostsRemoteManager(VkontakteManager):
+
+ def fetch_user_wall(self, user, offset=0, count=None, filter='all', extended=False):
+
+ if filter not in ['owner','others','all']:
+ raise ValueError("Attribute 'fiter' has illegal value '%s'" % filter)
+ if count > 100:
+ raise ValueError("Attribute 'count' can not be more than 100")
+
+ kwargs = {
+ 'owner_id': user.remote_id,
+ 'filter': filter,
+ }
+
+ if offset:
+ kwargs.update({'offset': offset})
+ if count:
+ kwargs.update({'count': count})
+ if extended:
+ kwargs.update({'extended': 1})
+ return self.fetch(**kwargs)
+
+ def fetch_group_wall(self, group, offset=0, count=None, own=False, after=None):
+ post_data = {
+ 'al':1,
+ 'offset': offset,
+ 'own': int(own), # posts by only group or any users
+ 'part': 1, # without header, footer
+ }
+ parser = VkontakteWallParser().request('/wall-%s' % group.remote_id, data=post_data)
+
+ items = parser.content_bs.findAll('div', {'class': re.compile('^post'), 'id': re.compile('^post-%d' % group.remote_id)})
+
+ current_count = offset + len(items)
+ need_cut = count and count < current_count
+ if need_cut:
+ items = items[:count-offset]
+
+ for item in items:
+
+ try:
+ post = parser.parse_post(item, group)
+ except VkontakteParseError, e:
+ log.error(e)
+ continue
+
+ post.raw_html = unicode(item)
+ post.save()
+ parsed.send(sender=Post, instance=post, container=item)
+
+ if after and post.date < after:
+ need_cut = True
+ break
+
+ if len(items) == 20 and not need_cut:
+ return self.fetch_group_wall(group, offset=current_count, count=count, own=own, after=after)
+ elif after and need_cut:
+ return group.wall_posts.filter(date__gte=after)
+ else:
+ return group.wall_posts.all()
+
+class VkontakteCommentsRemoteManager(VkontakteManager):
+
+ def fetch_user_post(self, post, offset=0, count=None):
+ if count > 100:
+ raise ValueError("Attribute 'count' can not be more than 100")
+
+ kwargs = {
+ 'owner_id': post.wall_owner.remote_id,
+ 'post_id': post.remote_id.split('_')[1],
+ 'preview_length': 0,
+ 'sort': 'asc',
+ }
+
+ if offset:
+ kwargs.update({'offset': offset})
+ if count:
+ kwargs.update({'count': count})
+
+ instances = self.get(**kwargs)
+ instances_saved = []
+
+ for instance in instances:
+ instance.post = post
+ instance.fetched = datetime.now()
+ instances_saved += [self.get_or_create_from_instance(instance)]
+
+ return instances_saved
+
+ def fetch_group_post(self, post, offset=0, count=None):#, after=None, only_new=False):
+ post_data = {
+ 'al':1,
+ 'offset': offset,
+ 'part': 1,
+ }
+ parser = VkontakteWallParser().request('/wall%s' % (post.remote_id), data=post_data)
+
+ items = parser.content_bs.findAll('div', {'class': 'fw_reply'})
+
+ current_count = offset + len(items)
+ need_cut = count and count < current_count
+ if need_cut:
+ items = items[:count-offset]
+
+# # get date of last comment and set after attribute
+# if only_new:
+# comments = post.wall_comments.order_by('-date')
+# if comments:
+# after = comments[0].date
+
+ for item in items:
+
+ try:
+ comment = parser.parse_comment(item, post.wall_owner)
+ except VkontakteParseError, e:
+ log.error(e)
+ continue
+
+ comment.post = post
+ comment.raw_html = unicode(item)
+ comment.save()
+ parsed.send(sender=Comment, instance=comment, container=item)
+
+# if after and comment.date < after:
+# need_cut = True
+# break
+
+ if len(items) == 20 and not need_cut:
+ return self.fetch_group_post(post, offset=current_count, count=count)#, after=after, only_new=only_new)
+# elif after and need_cut:
+# return post.wall_comments.filter(date__gte=after)
+ else:
+ if not count:
+ post.comments = post.wall_comments.count()
+ post.save()
+ return post.wall_comments.all()
+
+class VkontakteWallModel(VkontakteModel):
+ class Meta:
+ abstract = True
+
+ methods_namespace = 'wall'
+
+ # only for posts/comments from parser
+ raw_html = models.TextField()
+
+ @property
+ def slug(self):
+ return 'wall%s' % self.remote_id
+
+class Post(VkontakteWallModel):
+ class Meta:
+ db_table = 'vkontakte_wall_post'
+ verbose_name = u'Сообщение Вконтакте'
+ verbose_name_plural = u'Сообщения Вконтакте'
+ ordering = ['wall_owner_id','-date']
+
+ remote_id = models.CharField(u'ID', max_length='20', help_text=u'Уникальный идентификатор', unique=True)
+
+ # Владелец стены сообщения User or Group
+ wall_owner_content_type = models.ForeignKey(ContentType, related_name='vkontakte_wall_posts')
+ wall_owner_id = models.PositiveIntegerField()
+ wall_owner = generic.GenericForeignKey('wall_owner_content_type', 'wall_owner_id')
+
+ # Создатель/автор сообщения
+ author_content_type = models.ForeignKey(ContentType, related_name='vkontakte_posts')
+ author_id = models.PositiveIntegerField()
+ author = generic.GenericForeignKey('author_content_type', 'author_id')
+
+ # abstract field for correct deleting group and user models in admin
+ group_wall = generic.GenericForeignKey('wall_owner_content_type', 'wall_owner_id')
+ user_wall = generic.GenericForeignKey('wall_owner_content_type', 'wall_owner_id')
+ group = generic.GenericForeignKey('author_content_type', 'author_id')
+ user = generic.GenericForeignKey('author_content_type', 'author_id')
+
+ date = models.DateTimeField(u'Время сообщения')
+ text = models.TextField(u'Текст записи')
+
+ comments = models.PositiveIntegerField(u'Кол-во комментариев', default=0)
+ likes = models.PositiveIntegerField(u'Кол-во лайков', default=0)
+ reposts = models.PositiveIntegerField(u'Кол-во репостов', default=0)
+
+ like_users = models.ManyToManyField(User, blank=True, related_name='like_posts')
+ repost_users = models.ManyToManyField(User, blank=True, related_name='repost_posts')
+
+ #{u'photo': {u'access_key': u'5f19dfdc36a1852824',
+ #u'aid': -7,
+ #u'created': 1333664090,
+ #u'height': 960,
+ #u'owner_id': 2462759,
+ #u'pid': 281543621,
+ #u'src': u'http://cs9733.userapi.com/u2462759/-14/m_fdad45ec.jpg',
+ #u'src_big': u'http://cs9733.userapi.com/u2462759/-14/x_60b1aed1.jpg',
+ #u'src_small': u'http://cs9733.userapi.com/u2462759/-14/s_d457021e.jpg',
+ #u'src_xbig': u'http://cs9733.userapi.com/u2462759/-14/y_b5a67b8d.jpg',
+ #u'src_xxbig': u'http://cs9733.userapi.com/u2462759/-14/z_5a64a153.jpg',
+ #u'text': u'',
+ #u'width': 1280},
+ #u'type': u'photo'}
+
+ #u'attachments': [{u'link': {u'description': u'',
+ #u'image_src': u'http://cs6030.userapi.com/u2462759/-2/x_cb9c00f8.jpg',
+ #u'title': u'SAAB_9000_CD_2_0_Turbo_190_k.jpg',
+ #u'url': u'http://www.yauto.cz/includes/img/inzerce/SAAB_9000_CD_2_0_Turbo_190_k.jpg'},
+ #u'type': u'link'}],
+ #attachments - содержит массив объектов, которые присоединены к текущей записи (фотографии, ссылки и т.п.). Более подробная информация представлена на странице Описание поля attachments
+ attachments = models.TextField()
+ media = models.TextField()
+
+ #{u'coordinates': u'55.6745689498 37.8724562529',
+ #u'place': {u'city': u'Moskovskaya oblast',
+ #u'country': u'Russian Federation',
+ #u'title': u'Shosseynaya ulitsa, Moskovskaya oblast'},
+ #u'type': u'point'}
+ #geo - если в записи содержится информация о местоположении, то она будет представлена в данном поле. Более подробная информация представлена на странице Описание поля geo
+ geo = models.TextField()
+
+ signer_id = models.PositiveIntegerField(null=True, help_text=u'Eсли запись была опубликована от имени группы и подписана пользователем, то в поле содержится идентификатор её автора')
+ # могут быть негативные id, это группы или страницы
+ copy_owner_id = models.IntegerField(null=True, help_text=u'Eсли запись является копией записи с чужой стены, то в поле содержится идентификатор владельца стены у которого была скопирована запись')
+ copy_post_id = models.PositiveIntegerField(null=True, help_text=u'Если запись является копией записи с чужой стены, то в поле содержится идентфикатор скопированной записи на стене ее владельца')
+ copy_text = models.TextField(u'Комментарий при репосте', help_text=u'Если запись является копией записи с чужой стены и при её копировании был добавлен комментарий, его текст содержится в данном поле')
+
+ # not in API
+ post_source = models.TextField()
+ online = models.PositiveSmallIntegerField(null=True)
+ reply_count = models.PositiveSmallIntegerField(null=True)
+
+ objects = models.Manager()
+ remote = VkontaktePostsRemoteManager(remote_pk=('remote_id',), methods={
+ 'get': 'get',
+ })
+
+ @property
+ def on_group_wall(self):
+ return self.wall_owner_content_type == ContentType.objects.get_for_model(Group)
+ @property
+ def on_user_wall(self):
+ return self.wall_owner_content_type == ContentType.objects.get_for_model(User)
+
+ def __unicode__(self):
+ return '%s: %s' % (unicode(self.wall_owner), self.text)
+
+ def save(self, *args, **kwargs):
+ # check strings for good encoding
+ # there is problems to save users with bad encoded activity strings like user ID=88798245
+# try:
+# self.text.encode('utf-16').decode('utf-16')
+# except UnicodeDecodeError:
+# self.text = ''
+
+ # TODO: move this checking and other one to universal place
+ # set exactly right Group or User contentTypes, not a child
+ for field_name in ['wall_owner', 'author']:
+ for allowed_model in [Group, User]:
+ if isinstance(getattr(self, field_name), allowed_model):
+ setattr(self, '%s_content_type' % field_name, ContentType.objects.get_for_model(allowed_model))
+ break
+
+ # check is generic fields has correct content_type
+ allowed_ct_ids = [ct.id for ct in ContentType.objects.get_for_models(Group, User).values()]
+ if self.wall_owner_content_type.id not in allowed_ct_ids:
+ raise ValueError("'wall_owner' field should be Group or User instance, not %s" % self.wall_owner_content_type)
+ if self.author_content_type.id not in allowed_ct_ids:
+ raise ValueError("'author' field should be Group or User instance, not %s" % self.author_content_type)
+
+ return super(Post, self).save(*args, **kwargs)
+
+ def parse(self, response):
+
+ for field_name in ['comments','likes','reposts']:
+ if field_name in response and 'count' in response[field_name]:
+ setattr(self, field_name, response.pop(field_name)['count'])
+
+ # parse over API only for user's walls
+ self.wall_owner = User.objects.get_or_create(remote_id=response.pop('to_id'))[0]
+ self.author = User.objects.get_or_create(remote_id=response.pop('from_id'))[0]
+
+ if 'attachment' in response:
+ response.pop('attachment')
+ super(Post, self).parse(response)
+
+ self.remote_id = '%s_%s' % (self.wall_owner.remote_id, self.remote_id)
+
+ def fetch_comments(self, *args, **kwargs):
+ if self.on_group_wall:
+ return Comment.remote.fetch_group_post(self, *args, **kwargs)
+ elif self.on_user_wall:
+ return Comment.remote.fetch_user_post(self, *args, **kwargs)
+
+ def update_likes(self, offset=0):
+ '''
+ Update fields:
+ * likes - count of likes
+ * like_users - users, who likes this post
+ '''
+
+ post_data = {
+ 'act':'a_get_members',
+ 'al': 1,
+ 'object': 'wall%s' % self.remote_id,
+ 'only_content': 1,
+ 'published': 0,
+ 'tab': 0,
+ 'offset': offset,
+ }
+ parser = VkontakteWallParser().request('/like.php', data=post_data)
+
+ if offset == 0:
+ title = parser.content_bs.find('h4')
+ if title:
+ try:
+ self.likes = int(title.text.split(' ')[1])
+ self.save()
+ except:
+ log.warning('Strange format of h4 container: "%s"' % title.text)
+ self.like_users.clear()
+
+ avatars = parser.content_bs.findAll('div', {'class': 'liked_box_row'})
+ for avatar in avatars:
+ user = User.remote.get_by_slug(avatar.findAll('a')[1]['href'][1:])
+ if user:
+ user.first_name = avatar.findAll('a')[1].text
+ user.photo = avatar.find('img')['src']
+ user.save()
+ self.like_users.add(user)
+
+ if len(avatars) == 24:
+ self.update_likes(offset=offset+24)
+
+ def update_reposts(self, offset=0):
+ '''
+ Update fields:
+ * reposts - count of reposts
+ * repost_users - users, who repost this post
+ '''
+ post_data = {
+ 'act':'a_get_members',
+ 'al': 1,
+ 'object': 'wall%s' % self.remote_id,
+ 'only_content': 1,
+ 'published': 1,
+ 'tab': 1,
+ 'offset': offset,
+ }
+ parser = VkontakteWallParser().request('/like.php', data=post_data)
+
+ if offset == 0:
+ title = parser.content_bs.find('h4')
+ if title:
+ self.reposts = int(title.text.split(' ')[0])
+ self.save()
+ self.repost_users.clear()
+
+ avatars = parser.content_bs.findAll('div', {'class': 'liked_box_row'})
+ for avatar in avatars:
+ user = User.remote.get_by_slug(avatar.findAll('a')[1]['href'][1:])
+ if user:
+ user.first_name = avatar.findAll('a')[1].text
+ user.photo = avatar.find('img')['src']
+ user.save()
+ self.repost_users.add(user)
+
+ if len(avatars) == 24:
+ self.update_reposts(offset=offset+24)
+
+class Comment(VkontakteWallModel):
+ class Meta:
+ db_table = 'vkontakte_wall_comment'
+ verbose_name = u'Коментарий Вконтакте'
+ verbose_name_plural = u'Комментарии Вконтакте'
+ ordering = ['post','-date']
+
+ remote_pk_field = 'cid'
+
+ remote_id = models.CharField(u'ID', max_length='20', help_text=u'Уникальный идентификатор', unique=True)
+
+ post = models.ForeignKey(Post, verbose_name=u'Пост', related_name='wall_comments')
+
+ # Автор комментария
+ author_content_type = models.ForeignKey(ContentType, related_name='comments')
+ author_id = models.PositiveIntegerField()
+ author = generic.GenericForeignKey('author_content_type', 'author_id')
+
+ from_id = models.IntegerField(null=True) # strange value, seems to be equal to author
+
+ # Это ответ пользователю
+ reply_for_content_type = models.ForeignKey(ContentType, null=True, related_name='replies')
+ reply_for_id = models.PositiveIntegerField(null=True)
+ reply_for = generic.GenericForeignKey('reply_for_content_type', 'reply_for_id')
+
+ reply_to = models.ForeignKey('self', null=True, verbose_name=u'Это ответ на комментарий')
+
+ # abstract field for correct deleting group and user models in admin
+ group = generic.GenericForeignKey('author_content_type', 'author_id')
+ user = generic.GenericForeignKey('author_content_type', 'author_id')
+ group_wall_reply = generic.GenericForeignKey('reply_for_content_type', 'reply_for_id')
+ user_wall_reply = generic.GenericForeignKey('reply_for_content_type', 'reply_for_id')
+
+ date = models.DateTimeField(u'Время комментария')
+ text = models.TextField(u'Текст комментария')
+
+ likes = models.PositiveIntegerField(u'Кол-во лайков', default=0)
+
+ objects = models.Manager()
+ remote = VkontakteCommentsRemoteManager(remote_pk=('remote_id',), methods={
+ 'get': 'getComments',
+ })
+
+ def save(self, *args, **kwargs):
+ # it's here, because self.post is not in API response
+ if '_' not in str(self.remote_id):
+ self.remote_id = '%s_%s' % (self.post.remote_id.split('_')[0], self.remote_id)
+
+ # TODO: move this checking and other one to universal place
+ # set exactly right Group or User contentTypes, not a child
+ for field_name in ['reply_for', 'author']:
+ for allowed_model in [Group, User]:
+ if isinstance(getattr(self, field_name), allowed_model):
+ setattr(self, '%s_content_type' % field_name, ContentType.objects.get_for_model(allowed_model))
+ break
+
+ allowed_ct_ids = [ct.id for ct in ContentType.objects.get_for_models(Group, User).values()]
+ if self.author_content_type.id not in allowed_ct_ids:
+ raise ValueError("'author' field should be Group or User instance, not %s" % self.author_content_type)
+ if self.reply_for_content_type and self.reply_for_content_type.id not in allowed_ct_ids:
+ raise ValueError("'reply_for' field should be Group or User instance, not %s" % self.reply_for_content_type)
+
+ return super(Comment, self).save(*args, **kwargs)
+
+ def parse(self, response):
+ super(Comment, self).parse(response)
+
+ for field_name in ['likes']:
+ if field_name in response and 'count' in response[field_name]:
+ setattr(self, field_name, response.pop(field_name)['count'])
+
+ self.author = User.objects.get_or_create(remote_id=response['uid'])[0]
+
+ if 'reply_to_uid' in response:
+ self.reply_for = User.objects.get_or_create(remote_id=response['reply_to_uid'])[0]
+ if 'reply_to_cid' in response:
+ try:
+ self.reply_to = Comment.objects.get(remote_id=response['reply_to_cid'])
+ except:
+ pass
+
+Group.add_to_class('wall_posts', generic.GenericRelation(Post, content_type_field='wall_owner_content_type', object_id_field='wall_owner_id', related_name='group_wall', verbose_name=u'Сообщения на стене'))
+User.add_to_class('wall_posts', generic.GenericRelation(Post, content_type_field='wall_owner_content_type', object_id_field='wall_owner_id', related_name='user_wall', verbose_name=u'Сообщения на стене'))
+
+Group.add_to_class('posts', generic.GenericRelation(Post, content_type_field='author_content_type', object_id_field='author_id', related_name='group', verbose_name=u'Сообщения'))
+User.add_to_class('posts', generic.GenericRelation(Post, content_type_field='author_content_type', object_id_field='author_id', related_name='user', verbose_name=u'Сообщения'))
+
+Group.add_to_class('comments', generic.GenericRelation(Comment, content_type_field='author_content_type', object_id_field='author_id', related_name='group', verbose_name=u'Комментарии'))
+User.add_to_class('comments', generic.GenericRelation(Comment, content_type_field='author_content_type', object_id_field='author_id', related_name='user', verbose_name=u'Комментарии'))
+
+Group.add_to_class('replies', generic.GenericRelation(Comment, content_type_field='reply_for_content_type', object_id_field='reply_for_id', related_name='group_wall_reply', verbose_name=u'Ответы на комментарии'))
+User.add_to_class('replies', generic.GenericRelation(Comment, content_type_field='reply_for_content_type', object_id_field='reply_for_id', related_name='user_wall_reply', verbose_name=u'Ответы на комментарии'))
139 vkontakte_wall/parser.py
@@ -0,0 +1,139 @@
+# -*- coding: utf-8 -*-
+from datetime import datetime
+from vkontakte_api.parser import VkontakteParser, VkontakteParseError
+import re
+
+def get_object_by_slug(slug):
+ from vkontakte_users.models import User
+ from vkontakte_groups.models import Group
+ instance = User.remote.get_by_slug(slug)
+ if not instance:
+ instance = Group.remote.get_by_slug(slug)
+ return instance
+
+class VkontakteWallParser(VkontakteParser):
+
+ def parse_container_date(self, container):
+
+ text = container.find('span', {'class': re.compile('^rel_date')})
+ if text:
+ text = text.text
+ else:
+ raise VkontakteParseError("Impossible to find date container in %s" % container)
+ return datetime(1970,1,1)
+
+ return self.parse_date(text)
+
+ def parse_comment(self, content, wall_owner=None):
+ from models import Comment
+
+ remote_id = content['id'][4:]
+ try:
+ instance = Comment.objects.get(remote_id=remote_id)
+ except Comment.DoesNotExist:
+ instance = Comment(remote_id=remote_id)
+
+ comment_text = content.find('div', {'class': 'fw_reply_text'})
+ if comment_text:
+ instance.text = comment_text.text
+
+ # date
+ instance.date = self.parse_container_date(content)
+ # likes
+ instance.likes = self.parse_container_likes(content, 'like_count fl_l')
+
+ # author
+ users = content.findAll('a', {'class': 'fw_reply_author'})
+ slug = users[0]['href'][1:]
+ if wall_owner and wall_owner.screen_name == slug:
+ instance.author = wall_owner
+ else:
+ avatar = content.find('a', {'class': 'fw_reply_thumb'}).find('img')['src']
+ name_parts = users[0].text.split(' ')
+
+ user = get_object_by_slug(slug)
+ if user:
+ user.first_name = name_parts[0]
+ if len(name_parts) > 1:
+ user.last_name = name_parts[1]
+ user.photo = avatar
+ user.save()
+ instance.author = user
+
+ if len(users) == 2:
+ # this comment is answer
+ slug = users[1]['href'][1:]
+ if wall_owner and wall_owner.screen_name == slug:
+ instance.reply_for = wall_owner
+ else:
+ instance.reply_for = get_object_by_slug(slug)
+ # имя в падеже, аватара нет
+ # чтобы получть текст и ID родительского коммента нужно отправить:
+ #http://vk.com/al_wall.php
+ #act:post_tt
+ #al:1
+ #post:-16297716_126263
+ #reply:1
+
+ instance.fetched = datetime.now()
+ return instance
+
+ def parse_post(self, content, wall_owner):
+ from models import Post
+ from vkontakte_users.models import User
+
+ remote_id = content['id'][4:]
+ try:
+ instance = Post.objects.get(remote_id=remote_id)
+ except Post.DoesNotExist:
+ instance = Post(remote_id=remote_id)
+
+ post_text = content.find('div', {'class': 'wall_post_text'})
+ if post_text:
+ instance.text = post_text.text
+
+ # date
+ instance.date = self.parse_container_date(content)
+ # likes
+ instance.likes = self.parse_container_likes(content, 'post_like_count fl_l')
+
+ # comments
+ show_comments = content.find('div', {'class': 'wrh_text'})
+ if show_comments:
+ comments_words = show_comments.text.split(' ')
+ if len(comments_words) in [3,4]:
+ # Показать все 95 комментариев
+ # Показать 91 комментарий
+ instance.comments = int(comments_words[-2])
+ elif len(comments_words) == 6:
+ # Показать последние 100 комментариев из 170
+ instance.comments = int(comments_words[-1])
+ else:
+ raise VkontakteParseError("Error number of words in show all comments message: '%s'" % show_comments.text.encode('utf-8'))
+ else:
+ instance.comments = len(content.findAll('div', {'class': 'reply_text'}))
+
+ # author
+ owner_slug = content.find('a', {'class': 'author'})['href'][1:]
+ if wall_owner and wall_owner.screen_name == owner_slug:
+ instance.author = wall_owner
+ else:
+ # author is someone else,
+ # possible user, becouse the group can post only on it's own wall, where wall_owner is defined
+ avatar = content.find('a', {'class': 'post_image'}).find('img')['src']
+ name_parts = content.find('a', {'class': 'author'}).text.split(' ')
+
+ user = get_object_by_slug(owner_slug)
+ if user:
+ user.first_name = name_parts[0]
+ if len(name_parts) > 1:
+ user.last_name = name_parts[1]
+ user.photo = avatar
+ user.save()
+ instance.author = user
+
+ instance.fetched = datetime.now()
+ if wall_owner:
+ instance.wall_owner = wall_owner
+
+ return instance
180 vkontakte_wall/tests.py
@@ -0,0 +1,180 @@
+# -*- coding: utf-8 -*-
+from django.test import TestCase
+from models import Post, Comment
+from factories import PostFactory, UserFactory, GroupFactory
+from vkontakte_users.models import User
+from datetime import datetime
+import simplejson as json
+
+USER_ID = 18658732
+POST_ID = '18658732_2019'
+GROUP_ID = 16297716
+GROUP_SCREEN_NAME = 'cocacola'
+GROUP_POST_ID = '-16297716_126261'
+OPEN_WALL_GROUP_ID = '19391365'
+OPEN_WALL_GROUP_SCREEN_NAME = 'nokia'
+
+
+class VkontakteWallTest(TestCase):
+
+ def test_fetch_user_wall(self):
+
+ owner = UserFactory.create(remote_id=USER_ID)
+
+ self.assertEqual(Post.objects.count(), 0)
+
+ posts = owner.fetch_posts()
+
+ self.assertTrue(len(posts) > 0)
+ self.assertEqual(Post.objects.count(), len(posts))
+ self.assertEqual(posts[0].wall_owner, owner)
+
+ def test_fetch_group_wall(self):
+
+ group = GroupFactory.create(remote_id=GROUP_ID, screen_name=GROUP_SCREEN_NAME)
+
+ self.assertEqual(Post.objects.count(), 0)
+
+ posts = group.fetch_posts(count=10)
+
+ self.assertTrue(len(posts), 10)
+ self.assertEqual(Post.objects.count(), 10)
+ self.assertEqual(posts[0].wall_owner, group)
+ self.assertTrue(isinstance(posts[0].date, datetime))
+ self.assertTrue(posts[0].likes + posts[1].likes > 0)
+ self.assertTrue(posts[0].comments + posts[1].comments > 0)
+ self.assertTrue(len(posts[0].text) > 0)
+
+ def test_fetch_group_open_wall(self):
+
+ group = GroupFactory.create(remote_id=OPEN_WALL_GROUP_ID, screen_name=OPEN_WALL_GROUP_SCREEN_NAME)
+
+ self.assertEqual(Post.objects.count(), 0)
+ self.assertEqual(User.objects.count(), 0)
+
+ count = 10
+ posts = group.fetch_posts(own=0, count=count)
+
+ self.assertEqual(len(posts), count)
+ self.assertEqual(Post.objects.count(), count)
+ self.assertTrue(User.objects.count() > 0)
+ self.assertTrue(Post.objects.exclude(author_id=None).count() > 0)
+
+ def test_fetch_user_post_comments(self):
+
+ owner = UserFactory.create(remote_id=USER_ID)
+ post = PostFactory.create(remote_id=POST_ID, wall_owner=owner, author=owner)
+ self.assertEqual(Comment.objects.count(), 0)
+
+ comments = post.fetch_comments()
+
+ self.assertTrue(len(comments) > 0)
+ self.assertEqual(Comment.objects.count(), len(comments))
+ self.assertEqual(comments[0].post, post)
+
+ def test_fetch_group_post_comments(self):
+
+ group = GroupFactory.create(remote_id=GROUP_ID, screen_name=GROUP_SCREEN_NAME)
+ post = PostFactory.create(remote_id=GROUP_POST_ID, wall_owner=group)
+ self.assertEqual(Comment.objects.count(), 0)
+
+ comments = post.fetch_comments()
+
+ self.assertTrue(len(comments) > 0)
+ self.assertEqual(Comment.objects.count(), len(comments))
+ self.assertEqual(comments[0].post, post)
+ self.assertEqual(post.comments, len(comments))
+
+# def test_fetch_group_post_comments_after(self):
+#
+# group = GroupFactory.create(remote_id=GROUP_ID, screen_name=GROUP_SCREEN_NAME)
+# post = PostFactory.create(remote_id=GROUP_POST_ID, wall_owner=group)
+# self.assertEqual(Comment.objects.count(), 0)
+#
+# comments = post.fetch_comments(after=datetime(2012,7,23,0,0))
+#
+# self.assertTrue(len(comments) > 10)
+# self.assertEqual(Comment.objects.count(), len(comments))
+# self.assertEqual(comments[0].post, post)
+# self.assertEqual(post.comments, len(comments))
+
+ def test_update_post_reposts(self):
+
+ post = PostFactory.create(remote_id=GROUP_POST_ID)
+
+ self.assertEqual(post.reposts, 0)
+ self.assertEqual(post.repost_users.count(), 0)
+ post.update_reposts()
+ self.assertNotEqual(post.reposts, 0)
+ self.assertNotEqual(post.repost_users.count(), 0)
+
+ def test_update_post_likes(self):
+
+ post = PostFactory.create(remote_id=GROUP_POST_ID)
+
+ self.assertEqual(post.likes, 0)
+ self.assertEqual(post.like_users.count(), 0)
+ post.update_likes()
+ self.assertNotEqual(post.likes, 0)
+ self.assertNotEqual(post.like_users.count(), 0)
+ self.assertTrue(post.like_users.count() > 24)
+
+ def test_parse_post(self):
+
+ response = '''{"comments": {"can_post": 0, "count": 4},
+ "date": 1298365200,
+ "from_id": 55555,
+ "geo": {"coordinates": "55.6745689498 37.8724562529",
+ "place": {"city": "Moskovskaya oblast",
+ "country": "Russian Federation",
+ "title": "Shosseynaya ulitsa, Moskovskaya oblast"},
+ "type": "point"},
+ "id": 465,
+ "likes": {"can_like": 1, "can_publish": 1, "count": 10, "user_likes": 0},
+ "online": 1,
+ "post_source": {"type": "api"},
+ "reply_count": 0,
+ "reposts": {"count": 3, "user_reposted": 0},
+ "text": "qwerty",
+ "to_id": 2462759}
+ '''
+ instance = Post()
+ owner = UserFactory.create(remote_id=2462759)
+ author = UserFactory.create(remote_id=55555)
+ instance.parse(json.loads(response))
+ instance.save()
+
+ self.assertEqual(instance.remote_id, '2462759_465')
+ self.assertEqual(instance.wall_owner, owner)
+ self.assertEqual(instance.author, author)
+ self.assertEqual(instance.reply_count, 0)
+ self.assertEqual(instance.likes, 10)
+ self.assertEqual(instance.reposts, 3)
+ self.assertEqual(instance.comments, 4)
+ self.assertEqual(instance.text, 'qwerty')
+ self.assertEqual(instance.date, datetime(2011,2,22,12,0,0))
+
+ def test_parse_comments(self):
+
+ response = '''{"response":[6,
+ {"cid":2505,"uid":16271479,"date":1298365200,"text":"Добрый день , кароче такая идея когда опросы создаешь вместо статуса - можно выбрать аудитории опрашиваемых, например только женский или мужской пол могут участвовать (то бишь голосовать в опросе)."},
+ {"cid":2507,"uid":16271479,"date":1286105582,"text":"Это уже не практично, имхо.<br>Для этого делайте группу и там опрос, а в группу принимайте тех, кого нужно.","reply_to_uid":16271479,"reply_to_cid":2505},
+ {"cid":2547,"uid":2943,"date":1286218080,"text":"Он будет только для групп благотворительных организаций."}]}
+ '''
+ post = PostFactory(remote_id='1_0')
+ instance = Comment(post=post)
+ author = UserFactory.create(remote_id=16271479)
+ instance.parse(json.loads(response)['response'][1])
+ instance.save()
+
+ self.assertEqual(instance.remote_id, '1_2505')
+ self.assertEqual(instance.text, u'Добрый день , кароче такая идея когда опросы создаешь вместо статуса - можно выбрать аудитории опрашиваемых, например только женский или мужской пол могут участвовать (то бишь голосовать в опросе).')
+ self.assertEqual(instance.date, datetime(2011,2,22,12,0,0))
+ self.assertEqual(instance.author, author)
+
+ instance.parse(json.loads(response)['response'][2])
+ instance.save()
+
+ self.assertEqual(instance.remote_id, '1_2507')
+ self.assertEqual(instance.reply_for.remote_id, 16271479)
+# self.assertEqual(instance.reply_to.remote_id, '...2505')

0 comments on commit 68f4725

Please sign in to comment.
Something went wrong with that request. Please try again.