from datetime import datetime import json import logging from django.db import transaction from django.http import HttpResponse from django.shortcuts import get_object_or_404, render from django.template import RequestContext from django.utils import timezone from django.core import serializers from boards.forms import PostForm, PlainErrorList from boards.models import Post, Thread, Tag from boards.utils import datetime_to_epoch from boards.views.thread import ThreadView from boards.models.user import Notification __author__ = 'neko259' PARAMETER_TRUNCATED = 'truncated' PARAMETER_TAG = 'tag' PARAMETER_OFFSET = 'offset' PARAMETER_DIFF_TYPE = 'type' DIFF_TYPE_HTML = 'html' DIFF_TYPE_JSON = 'json' STATUS_OK = 'ok' STATUS_ERROR = 'error' logger = logging.getLogger(__name__) @transaction.atomic def api_get_threaddiff(request, thread_id, last_update_time): """ Gets posts that were changed or added since time """ thread = get_object_or_404(Post, id=thread_id).get_thread() # Add 1 to ensure we don't load the same post over and over last_update_timestamp = float(last_update_time) + 1 filter_time = datetime.fromtimestamp(last_update_timestamp / 1000000, timezone.get_current_timezone()) json_data = { 'added': [], 'updated': [], 'last_update': None, } added_posts = Post.objects.filter(threads__in=[thread], pub_time__gt=filter_time) \ .order_by('pub_time') updated_posts = Post.objects.filter(threads__in=[thread], pub_time__lte=filter_time, last_edit_time__gt=filter_time) diff_type = request.GET.get(PARAMETER_DIFF_TYPE, DIFF_TYPE_HTML) for post in added_posts: json_data['added'].append(get_post_data(post.id, diff_type, request)) for post in updated_posts: json_data['updated'].append(get_post_data(post.id, diff_type, request)) json_data['last_update'] = datetime_to_epoch(thread.last_edit_time) return HttpResponse(content=json.dumps(json_data)) def api_add_post(request, opening_post_id): """ Adds a post and return the JSON response for it """ opening_post = get_object_or_404(Post, id=opening_post_id) logger.info('Adding post via api...') status = STATUS_OK errors = [] if request.method == 'POST': form = PostForm(request.POST, request.FILES, error_class=PlainErrorList) form.session = request.session if form.need_to_ban: # Ban user because he is suspected to be a bot # _ban_current_user(request) status = STATUS_ERROR if form.is_valid(): post = ThreadView().new_post(request, form, opening_post, html_response=False) if not post: status = STATUS_ERROR else: logger.info('Added post #%d via api.' % post.id) else: status = STATUS_ERROR errors = form.as_json_errors() response = { 'status': status, 'errors': errors, } return HttpResponse(content=json.dumps(response)) def get_post(request, post_id): """ Gets the html of a post. Used for popups. Post can be truncated if used in threads list with 'truncated' get parameter. """ post = get_object_or_404(Post, id=post_id) context = RequestContext(request) context['post'] = post if PARAMETER_TRUNCATED in request.GET: context[PARAMETER_TRUNCATED] = True # TODO Use dict here return render(request, 'boards/api_post.html', context_instance=context) def api_get_threads(request, count): """ Gets the JSON thread opening posts list. Parameters that can be used for filtering: tag, offset (from which thread to get results) """ if PARAMETER_TAG in request.GET: tag_name = request.GET[PARAMETER_TAG] if tag_name is not None: tag = get_object_or_404(Tag, name=tag_name) threads = tag.get_threads().filter(archived=False) else: threads = Thread.objects.filter(archived=False) if PARAMETER_OFFSET in request.GET: offset = request.GET[PARAMETER_OFFSET] offset = int(offset) if offset is not None else 0 else: offset = 0 threads = threads.order_by('-bump_time') threads = threads[offset:offset + int(count)] opening_posts = [] for thread in threads: opening_post = thread.get_opening_post() # TODO Add tags, replies and images count post_data = get_post_data(opening_post.id, include_last_update=True) post_data['bumpable'] = thread.can_bump() post_data['archived'] = thread.archived opening_posts.append(post_data) return HttpResponse(content=json.dumps(opening_posts)) # TODO Test this def api_get_tags(request): """ Gets all tags or user tags. """ # TODO Get favorite tags for the given user ID tags = Tag.objects.get_not_empty_tags() tag_names = [] for tag in tags: tag_names.append(tag.name) return HttpResponse(content=json.dumps(tag_names)) # TODO The result can be cached by the thread last update time # TODO Test this def api_get_thread_posts(request, opening_post_id): """ Gets the JSON array of thread posts """ opening_post = get_object_or_404(Post, id=opening_post_id) thread = opening_post.get_thread() posts = thread.get_replies() json_data = { 'posts': [], 'last_update': None, } json_post_list = [] for post in posts: json_post_list.append(get_post_data(post.id)) json_data['last_update'] = datetime_to_epoch(thread.last_edit_time) json_data['posts'] = json_post_list return HttpResponse(content=json.dumps(json_data)) def api_get_notifications(request, username): last_notification_id_str = request.GET.get('last', None) last_id = int(last_notification_id_str) if last_notification_id_str is not None else None posts = Notification.objects.get_notification_posts(username=username, last=last_id) json_post_list = [] for post in posts: json_post_list.append(get_post_data(post.id)) return HttpResponse(content=json.dumps(json_post_list)) def api_get_post(request, post_id): """ Gets the JSON of a post. This can be used as and API for external clients. """ post = get_object_or_404(Post, id=post_id) json = serializers.serialize("json", [post], fields=( "pub_time", "_text_rendered", "title", "text", "image", "image_width", "image_height", "replies", "tags" )) return HttpResponse(content=json) # TODO Remove this method and use post method directly def get_post_data(post_id, format_type=DIFF_TYPE_JSON, request=None, include_last_update=False): post = get_object_or_404(Post, id=post_id) return post.get_post_data(format_type=format_type, request=request, include_last_update=include_last_update)