def handle(self, *fixture_labels, **options): from datetime import datetime from glob import glob from twitter.models import HashTag, Tweet, TwitAccount from BeautifulSoup import BeautifulSoup as BS from dateutil.parser import parse as parse_date import re nickre = re.compile('@<a.+?>([\w_\d]+)<') errors = counter = 0 for d in fixture_labels: print "preparing to work on", d for f in glob("%s/*.xml" % d): soup = BS(open(f)) for status in soup.findAll('status'): try: uid = status.user.id.contents[0] ta,new = TwitAccount.objects.get_or_create(uid=uid) if new: ta.screen_name = status.user.screen_name.contents[0].lower() ta.save() # XXX mysql is completely lame. it can't handle datetime # with tz info so we have to kludge it and create a datetime # object w/o tz... i really hate mysql at times like this.-jk ts = list(parse_date(status.created_at.contents[0]).utctimetuple()[:7]) ts = datetime(*ts) t = Tweet( tid=status.id.contents[0], owner=ta, text=status.text.contents[0][0:255], timestamp=ts, ) t.save() for nick in nickre.findall(str(status.text.contents[0])): repacct,new = TwitAccount.objects.get_or_create(screen_name=nick.lower()) if new: repacct.uid = -1 repacct.save() t.to_users.add(repacct) counter += 1 if not counter % 20: print ".", if not counter % 300: print "loaded:%d errors:%d" % (counter, errors) u = "http://infolab.tamu.edu/stats/new/%s/%d" % ( "twit01import", counter ) _ = urllib2.urlopen(u).read() _.close() u = "http://infolab.tamu.edu/stats/new/%s/%d" % ( "twit01error", errors ) _ = urllib2.urlopen(u).read() _.close() except: errors += 1 # print "ERR: text=", status.text.contents[0] # sys.exit(1) print "Some exception...", status.prettify() # break print "\n-----------------------------\nSummary" print "tweets imported:",counter,"\nerrors encountered:",errors
def create_tweet(author, content, original_tweet): new_tweet = Tweet( author=author, content=content, original_tweet=original_tweet, ) new_tweet.save() return new_tweet
def process(request): tweet = request.POST["tweet"] t = Tweet( text=tweet, p_date=timezone.now(), user=request.user ) t.save() return HttpResponseRedirect(reverse('index'))
def setUp(self): self.users = { 'author': User.objects.create(username='******'), 'user1': User.objects.create(username='******'), 'user2': User.objects.create(username='******'), 'user3': User.objects.create(username='******'), } self.tweet = Tweet(author=self.users['author'], tweet_text='aaa', pub_date=timezone.now()) self.tweet.save()
def new_tweet(request): current_user = request.user tweet_title = request.data.get('tweet_title') tweet_text = request.data.get('tweet_text') if tweet_text == '' or tweet_text == '': return Response(status=status.HTTP_400_BAD_REQUEST) tweet = Tweet(author=current_user, tweet_title=tweet_title, tweet_text=tweet_text, pub_date=timezone.now()) tweet.save() return Response(status=status.HTTP_201_CREATED)
def postTweet(request): if request.method=='POST': form=tweetForm(request.POST) if form.is_valid(): username=form.cleaned_data['username'] textarea=form.cleaned_data['textarea'] p=Tweet(userName=username,content=textarea) p.save() return render_to_response('submit.html',{'user':username},context_instance=RequestContext(request)) form=tweetForm() return render_to_response('newTweet.html',{'form':form},context_instance=RequestContext(request))
def on_status(self, status): if ( db.session.query(Tweet).filter_by(tweet_id=status.id).count() == 0 and # TODO(Need to encode this into unicode.) len(str(status.text.encode("unicode_escape"))) < 1000 ): LOGGING.push("*" + status.user.name + "*: " + LOGGING.clean(status.text)) Tweet.store_tweet(status) self.num_tweets += 1 if self.num_tweets % 100 == 0: LOGGING.push("*" + str(self.num_tweets) + "* tweets have been collected.")
def postTweet(request): if request.method == 'POST': form = tweetForm(request.POST) if form.is_valid(): username = form.cleaned_data['username'] textarea = form.cleaned_data['textarea'] p = Tweet(userName=username, content=textarea) p.save() return render_to_response('submit.html', {'user': username}, context_instance=RequestContext(request)) form = tweetForm() return render_to_response('newTweet.html', {'form': form}, context_instance=RequestContext(request))
def tweet_view(request, slug): tweet = Tweet.objects.get(slug=slug) if request.method == 'POST': text = request.POST.get('tweet_comment') new_tweet = Tweet(author=request.user, body=text, replying_tweet=tweet) new_tweet.save() comments = Tweet.objects.filter(replying_tweet=tweet) context = { 'tweet': tweet, 'comments': comments, 'username': request.user.get_username() } return render(request, 'twitter/tweet.html', context)
def send_push_notifications(body, org_id, last_editor_id): """Sends SNS push notifications to an org's devices""" from core.models import TweetCheckUser, Device from twitter.models import Tweet last_editor = TweetCheckUser.objects.get(pk=last_editor_id) arn_list = Device.objects.filter(user__organization__id=org_id).exclude(user=last_editor) \ .values_list('arn', flat=True) conn = sns.connect_to_region('us-east-1') if len(body) > 35: body = body[:35].rsplit(' ', 1)[0]+'...' alert = '{0} added a new tweet for review: "{1}"'.format(last_editor.get_short_name(), body) message = { 'aps': { 'alert': alert, 'badge': Tweet.get_pending_count(org_id), 'sound': 'default' } } sns_request = {settings.APNS_ARN: json.dumps(message)} for arn in arn_list: conn.publish(message=json.dumps(sns_request), message_structure='json', target_arn=arn)
def batch_processing(cls, users_attributes, tweets_attributes): [ u_attr.update({ 'created_at': datetime.datetime.strptime(u_attr['created_at'], cls.DATETIME_FORMAT), 'modified': datetime.datetime.strptime(u_attr['modified'], cls.DATETIME_FORMAT) }) for u_attr in users_attributes ] users = User.create_or_update(*users_attributes) [ t_attr.update({ 'created_at': datetime.datetime.strptime(t_attr['created_at'], cls.DATETIME_FORMAT), "coordinates": t_attr.get("coordinates") if t_attr.get("coordinates") else getattr(users[k], "coordinates", []) }) for k, t_attr in enumerate(tweets_attributes) ] tweets = Tweet.create_or_update(*tweets_attributes) for k, tweet in enumerate(tweets): users[k].posts.connect(tweet)
def test_string_representation(self): twitter_account = TwitterAccount(name='Peter') tweet = Tweet( content='Erster Tweet, #cool!', created_at=datetime(2012, 12, 21, 19, 9, 0), account=twitter_account, ) self.assertEqual( str(tweet), 'Peter at 2012-12-21 19:09:00 - Erster Tweet, #cool!')
def update_user_tweets_location(self, user): results, meta = db.cypher_query( 'MATCH (u:User)-[:POSTS]->(t:Tweet) WHERE u.id_str="%s" RETURN t' % user.id_str) user_tweets = [Tweet.inflate(row[0]) for row in results] for tweet in user_tweets: if tweet.coordinates == []: tweet.coordinates = user.coordinates tweet.save() self.updated_tweets.append(tweet)
def tweet(request): f = TweetForm(request.POST) if f.is_valid(): t = Tweet() t.user = request.user t.text = f.cleaned_data["text"] t.published_on = datetime.datetime.today() t.save() return HttpResponseRedirect(reverse('twitter:home'))
def create_tweet(): print("FORM DATA:", dict(request.form)) new_tweet = Tweet(content=request.form["content"], user=request.form["User_name"]) db.session.add(new_tweet) db.session.commit() return jsonify({ "message": "Successfully Created (TODO)", "book": dict(request.form) })
def tweet(request): if request.user.is_authenticated(): t = Tweet() t.user = request.user t.text = request.POST["text"] t.published_on = datetime.datetime.today() t.save() return HttpResponseRedirect(reverse('twitter:home')) else: return HttpResponseRedirect(reverse('twitter:login'))
def index(request): context = dict() if request.method == 'POST': if 'delete_tweet' in request.POST: _id = request.GET.get('id', None) Tweet.objects.filter(id=_id).delete() messages.success(request, "Tweet successfully deleted") return redirect('index') form = TweetForm(request.POST) if form.is_valid(): new_tweet = Tweet(username=request.user, tweet=request.POST['tweet']) try: new_tweet.full_clean() new_tweet.save() except ValidationError as e: context['error'] = e.message_dict['tweet'][0] else: messages.success(request, "Tweet Created!") return redirect('index') else: form = TweetForm() context['form'] = form context['tweets'] = Tweet.objects.filter( username=request.user).order_by('-post_date') return render(request, 'authenticated_user_feed.html', context)
def home(request): if request.method == "POST": canTweet = True error_tweet_30 = "" (canTweet, error_tweet_30) = userCanTweet(request) if canTweet: f = TweetForm(request.POST) if f.is_valid(): t = Tweet() t.user = request.user t.text = f.cleaned_data["text"] d = datetime.datetime.today() t.published_on = d t.save() request.session["time_of_last_tweet"] = d.strftime("%d %m %Y %H:%M:%S") return HttpResponseRedirect(reverse("twitter:home")) else: tweets = Tweet.objects.order_by("-published_on")[:5] return render(request,'twitter/home.html', {"tweets":tweets, "user": request.user, "f": f}) else: f = TweetForm() tweets = Tweet.objects.order_by("-published_on")[:5] return render(request,'twitter/home.html', {"tweets":tweets, "user": request.user, "f": f, "er30":error_tweet_30}) else: f = TweetForm() tweets = Tweet.objects.order_by("-published_on")[:5] return render(request,'twitter/home.html', {"tweets":tweets, "user": request.user, "f": f})
def home(request): if request.method == "POST": canTweet = True error_tweet_30 = "" if request.session.has_key("time_of_last_tweet"): old_time = datetime.datetime.strptime( request.session["time_of_last_tweet"], "%d %m %Y %H:%M:%S") delta = datetime.datetime.today() - old_time print datetime.datetime.today() print old_time print delta print delta.total_seconds() print delta.seconds if delta.total_seconds() < 30: canTweet = False error_tweet_30 = "You have tweeted " + str(int(delta.total_seconds())) + \ " seconds ago." if canTweet: f = TweetForm(request.POST) if f.is_valid(): t = Tweet() t.user = request.user t.text = f.cleaned_data["text"] d = datetime.datetime.today() t.published_on = d t.save() request.session["time_of_last_tweet"] = d.strftime( "%d %m %Y %H:%M:%S") return HttpResponseRedirect(reverse("twitter:home")) else: tweets = Tweet.objects.order_by("-published_on")[:5] return render(request, 'twitter/home.html', { "tweets": tweets, "user": request.user, "f": f }) else: f = TweetForm() tweets = Tweet.objects.order_by("-published_on")[:5] return render( request, 'twitter/home.html', { "tweets": tweets, "user": request.user, "f": f, "er30": error_tweet_30 }) else: f = TweetForm() tweets = Tweet.objects.order_by("-published_on")[:5] return render(request, 'twitter/home.html', { "tweets": tweets, "user": request.user, "f": f })
def run(): # create topic topic_example = TwTopic() topic_example.title = "migration" topic, created = TwTopic.objects.get_or_create(topic_example.__dict__) # create query string with well known migration organisations and their twitter accounts settings_dir = os.path.dirname(__file__) project_root = Path(os.path.dirname(settings_dir)).absolute() twitter_addresses_yaml = Path.joinpath(project_root, "twitter\\twitter_addresses.yaml") with open(twitter_addresses_yaml) as f: # use safe_load instead load data_map = yaml.safe_load(f) class TwitterAddresses: def __init__(self, entries): self.__dict__.update(entries) addresses = TwitterAddresses(data_map) pp = pprint.PrettyPrinter(indent=4) # query_string = "" # for (institution in addresses.institutions): # query_string+= institution.twitter_account # institutions = list(map(lambda x: list(x.values())[0], addresses.institutions)) institutions = addresses.institutions twitter_accounts = map(lambda x: x.get('institution').get('twitter_account').replace("@", ""), institutions) twitter_accounts_query_1 = map(lambda x: "(from:{}) OR ".format(x), twitter_accounts) twitter_accounts_query_2 = reduce(lambda x, y: x + y, twitter_accounts_query_1) twitter_accounts_query_2 += "#immigration lang:en" pp.pprint(twitter_accounts_query_2) params = {'query': '{}'.format(twitter_accounts_query_2), 'max_results': '500'} search_url = "https://api.twitter.com/2/tweets/search/all" connector = TwitterConnector(1) json_result = connector.get_from_twitter(search_url, params, True) # print(json.dumps(json_result, indent=4, sort_keys=True)) twitter_data: list = json_result.get("data") for tweet_raw in twitter_data: try: tweet = Tweet() tweet.topic = topic tweet.query_string = twitter_accounts_query_2 tweet.text = tweet_raw.get("text") tweet.twitter_id = tweet_raw.get("id") tweet.save() except IntegrityError: pass
def profile(request, username="******", template='profile/view.html', extra_context=None): user = get_object_or_404(User, username=username) tweets = Tweet.get_decorated_list(request.user.id, author=user) tags = Counter([ f'#{tag.name} ' for tweet in tweets for tag in tweet.tags.all() ]).most_common(20) context = {'user': user, 'tweets': tweets, 'tags': tags} if extra_context is not None: context.update(extra_context) return render(request, template, context)
def sync_tweets_and_users_save(data): for tweet in range(len(data)): tu = TwitterUser(id=data[tweet]['user']['id'], name=data[tweet]['user']['name'], screen_name=data[tweet]['user']['screen_name'], followers_count=data[tweet]['user']['followers_count'], friends_count=data[tweet]['user']['friends_count'], profile_image_url=data[tweet]['user']['profile_image_url'], profile_image_url_https=data[tweet]['user']['profile_image_url_https'], lang=data[tweet]['user']['lang'] ) tu.save() t = Tweet(id=data[tweet]['id'], twitter_user_id=data[tweet]['user']['id'], text=data[tweet]['text'], created_at=datetime.datetime.strptime(data[tweet]['created_at'], '%a %b %d %H:%M:%S +0000 %Y').replace(tzinfo=pytz.utc), favorite_count=data[tweet]['favorite_count'], favorited=data[tweet]['favorited'], retweet_count=data[tweet]['retweet_count'], lang=data[tweet]['lang'] ) t.save()
def parse_segment(path): """Parse a raw minute file. """ with gzip.open(fs.read(path)) as fh: for line in fh.readlines(): try: raw = ujson.loads(line) if raw['verb'] == 'post': yield Tweet.from_gnip_json(raw) except Exception as e: print(e)
def crawl_category(category): """Crawls a specific given category. Args: category: Category to search for. """ cursor = limit_handled( tweepy.Cursor(API.search, q=category, count=100).items(1000) ) for status in cursor: if ( db.session.query(Tweet).filter_by( tweet_id=status.id ).count() == 0 and # TODO(Need to encode this into unicode.) len(str(status.text.encode('unicode_escape'))) < 1000 ): LOGGING.push( "*" + status.user.name + "*: " + LOGGING.clean(status.text) ) Tweet.store_tweet(status)
def run(): # create topic topic_example = TwTopic() topic_example.title = "random" topic, created = TwTopic.objects.get_or_create(topic_example.__dict__) count = 500 connector = TwitterConnector(1) random_stream_url = "https://api.twitter.com/2/tweets/sample/stream" headers = connector.create_headers() params = {'tweet.fields': 'lang'} response = requests.request("GET", random_stream_url, headers=headers, stream=True, params=params) print("is connected {}".format(response.status_code)) if response.status_code != 200: raise Exception("Request returned an error: {} {}".format( response.status_code, response.text)) for response_line in response.iter_lines(): if response_line: json_response = json.loads(response_line) while True: if count == 0: break twitter_data: list = json_response.get("data") if twitter_data.get("lang") == "en": print(json.dumps(json_response, indent=4, sort_keys=True)) count += -1 try: tweet = Tweet() tweet.topic = topic tweet.query_string = topic_example.title tweet.text = twitter_data.get("text") tweet.twitter_id = twitter_data.get("id") tweet.save() except IntegrityError: pass
def search_term(self, term): from twitter.models import Tweet import json try: search_params = {'q':unicode(term), 'count':self.results_per_request} self.logger.debug('Searching with parameters %s' % search_params) search_service_path = "search/tweets.json" session = self.get_session() response = session.get(search_service_path, params=search_params).json() statuses = response['statuses'] info = { 'count': self.results_per_request, 'result_count': statuses.__len__(), 'search_counter': term.increment_search_counter(), 'results_saved_count': 0, 'results_duplicate_count': 0, 'results_error_count': 0, 'results_geotagged': 0, 'payload_size': json.dumps(response).__len__(), 'payload_path': self.store_payload(json.dumps(response)), } self.logger.debug('Got %s results' % info['result_count']) for result in statuses: tweet = Tweet.objects.get_or_none(pk=result['id']) if tweet: info['results_duplicate_count'] += 1 else: tweet = Tweet.create(result, term) try: tweet.user.save() tweet.save() info['results_saved_count'] += 1 except Exception as e: info['results_error_count'] += 1 logged_tweet_path = tweet.log() self.logger.exception("Could not save tweet with id %s to database. %s. JSON response logged to %s" % (tweet.id, e, logged_tweet_path)) if tweet.is_geotagged(): info['results_geotagged'] += 1 return {'info':info, 'statuses':statuses} except Exception as e: self.logger.exception(e) return None
def envoyer_tweet(request): if request.user.is_authenticated(): login= UserProfil.objects.get(pk = request.user.pk) if request == "POST": tweet_form = TweetForm(request.POST) if tweet_form.is_valide(): tweet = Tweet() tweet.message = tweet_form.cleaned_data['message'] tweet.date = datetime.date.today() tweet.user = login tweet.save() return redirect(user_profil) else: tweet_form = TweetForm() tweets = Tweet.objects.all().order_by('date') contexte = {'tweets': tweets, 'tweet_form' : tweet_form} return render(request,'bienvenue.html', contexte)
def create_user(screen_name=None): screen_name = request.form["user"] twitter_user = twitter_api_client.get_user(screen_name) statuses = twitter_api_client.user_timeline(screen_name, tweet_mode="extended", count=150, exclude_replies=True, include_rts=False) print("STATUSES COUNT:", len(statuses)) # get existing user from the db or initialize a new one: db_user = User.query.get(twitter_user.id) or User(id=twitter_user.id) db_user.screen_name = twitter_user.screen_name db_user.name = twitter_user.name db_user.location = twitter_user.location db_user.followers_count = twitter_user.followers_count db.session.add(db_user) db.session.commit() all_tweet_texts = [status.full_text for status in statuses] embeddings = list( basilica_api_client.embed_sentences(all_tweet_texts, model="twitter")) print("NUMBER OF EMBEDDINGS", len(embeddings)) # TODO: explore using the zip() function maybe... counter = 0 for status in statuses: print(status.full_text) print("----") # get existing tweet from the db or initialize a new one: db_tweet = Tweet.query.get(status.id) or Tweet(id=status.id) db_tweet.user_id = status.author.id db_tweet.full_text = status.full_text embedding = embeddings[counter] print(len(embedding)) db_tweet.embedding = embedding db.session.add(db_tweet) counter += 1 db.session.commit() return render_template("user.html", user=db_user, tweets=statuses)
def home(request): if request.method == "POST": f = TweetForm(request.POST) if f.is_valid(): t = Tweet() t.user = request.user t.text = f.cleaned_data["text"] t.published_on = datetime.datetime.today() t.save() else: tweets = Tweet.objects.order_by("-published_on")[:5] return render(request, 'twitter/home.html', { "tweets": tweets, "f": f }) else: f = TweetForm() tweets = Tweet.objects.order_by("-published_on")[:5] return render(request, 'twitter/home.html', {"tweets": tweets, "f": f})
def home(request): if request.method == "POST": canTweet = True error_tweet_30 = "" if request.session.has_key("time_of_last_tweet"): old_time = datetime.datetime.strptime(request.session["time_of_last_tweet"], "%d %m %Y %H:%M:%S") delta = datetime.datetime.today() - old_time print datetime.datetime.today() print old_time print delta print delta.total_seconds() print delta.seconds if delta.total_seconds() < 30: canTweet = False error_tweet_30 = "You have tweeted " + str(int(delta.total_seconds())) + \ " seconds ago." if canTweet: f = TweetForm(request.POST) if f.is_valid(): t = Tweet() t.user = request.user t.text = f.cleaned_data["text"] d = datetime.datetime.today() t.published_on = d t.save() request.session["time_of_last_tweet"] = d.strftime("%d %m %Y %H:%M:%S") return HttpResponseRedirect(reverse("twitter:home")) else: tweets = Tweet.objects.order_by("-published_on")[:5] return render(request,'twitter/home.html', {"tweets":tweets, "user": request.user, "f": f}) else: f = TweetForm() tweets = Tweet.objects.order_by("-published_on")[:5] return render(request,'twitter/home.html', {"tweets":tweets, "user": request.user, "f": f, "er30":error_tweet_30}) else: f = TweetForm() tweets = Tweet.objects.order_by("-published_on")[:5] return render(request,'twitter/home.html', {"tweets":tweets, "user": request.user, "f": f})
def home(request): if request.method == "POST": f = TweetForm(request.POST) if f.is_valid(): t = Tweet() t.user = request.user t.text = f.cleaned_data["text"] t.published_on = datetime.datetime.today() t.save() else: tweets = Tweet.objects.order_by("-published_on")[:5] return render(request,'twitter/home.html', {"tweets":tweets, "f": f}) else: f = TweetForm() tweets = Tweet.objects.order_by("-published_on")[:5] return render(request,'twitter/home.html', {"tweets":tweets, "f": f})
def batch_processing(cls, users_attributes, tweets_attributes): [ u_attr.update({ 'created_at': datetime.datetime.strptime(u_attr['created_at'], cls.DATETIME_FORMAT), 'modified': datetime.datetime.strptime(u_attr['modified'], cls.DATETIME_FORMAT) }) for u_attr in users_attributes ] users = User.create_or_update(*users_attributes) [ t_attr.update({ 'created_at': datetime.datetime.strptime(t_attr['created_at'], cls.DATETIME_FORMAT), "coordinates": t_attr.get("coordinates") if t_attr.get("coordinates") else getattr(users[k], "coordinates", []) }) for k, t_attr in enumerate(tweets_attributes) ] tweets = Tweet.create_or_update(*tweets_attributes) for k, tweet in enumerate(tweets): users[k].posts.connect(tweet) # Connect tweet with the company it mentions # company_name = cls.find_company(tweet.text) # company_node = Company.nodes.get(name=company_name) # tweet.tweet_about.connect(company_node) # for demo company_event = Event.nodes.get(name='Autopilot rolled out') company_event.tweet_from.connect(tweet)
def add(request): t = Tweet(message=request.POST['message'], pub_date=timezone.now(), author=request.user) t.save() return HttpResponseRedirect(reverse('twitter:index'))
class TweetTestCase(TestCase): def setUp(self): self.users = { 'author': User.objects.create(username='******'), 'user1': User.objects.create(username='******'), 'user2': User.objects.create(username='******'), 'user3': User.objects.create(username='******'), } self.tweet = Tweet(author=self.users['author'], tweet_text='aaa', pub_date=timezone.now()) self.tweet.save() def test_score(self): """tweet score is correctly calculated""" votes = [ Vote.objects.create(voter=self.users['user1'], tweet=self.tweet, direction=0), Vote.objects.create(voter=self.users['user2'], tweet=self.tweet, direction=0), Vote.objects.create(voter=self.users['user3'], tweet=self.tweet, direction=0), ] self.tweet.vote_set.set(votes) self.assertEqual(self.tweet.score, 0) new_directions = [-1, 1, 1] for vote, direction in zip(votes, new_directions): vote.direction = direction vote.save() self.assertEqual(self.tweet.score, sum(new_directions)) def test_decorating_list(self): pass # TODO def test_voting(self): """tweet voting works as intended""" user = self.users['user1'] result = self.tweet.make_vote(user, 1) self.assertTrue(result['created'], 'voting for the first time creates new vote') self.assertEqual(result['direction'], 1, 'voting sets vote direction correctly') self.assertEqual(result['updated_score'], 1, 'score is correctly updated') result = self.tweet.make_vote(user, 1) self.assertFalse(result['created'], 'subsequent votes do not create new vote instances') self.assertEqual( result['direction'], 0, 'subsequent vote with same direction sets direction to 0') self.assertEqual(result['updated_score'], 0, 'score is correctly updated') result = self.tweet.make_vote(user, 1) self.assertFalse(result['created'], 'subsequent votes do not create new vote instances') self.assertEqual(result['direction'], 1, 'voting sets new vote direction correctly') self.assertEqual(result['updated_score'], 1, 'score is correctly updated') result = self.tweet.make_vote(user, -1) self.assertFalse(result['created'], 'subsequent votes do not create new vote instances') self.assertEqual(result['direction'], -1, 'voting sets new vote direction correctly') self.assertEqual(result['updated_score'], -1, 'score is correctly updated') def tearDown(self): pass