|
| 1 | + |
| 2 | +from django import forms |
| 3 | +from django.http import HttpResponseRedirect, HttpResponse |
| 4 | +from django.shortcuts import render_to_response |
| 5 | +from django_restapi.model_resource import Collection |
| 6 | +from django.forms.util import ErrorDict |
| 7 | +from django.utils.translation.trans_null import _ |
| 8 | +from django.core import serializers |
| 9 | +from django.core.mail import send_mail |
| 10 | +from django.conf import settings |
| 11 | +from django.utils.simplejson import JSONEncoder, JSONDecoder |
| 12 | +import django.contrib.auth.models as authmodels |
| 13 | +from django_restapi.resource import Resource |
| 14 | +from django_restapi.model_resource import InvalidModelData |
| 15 | +from jv3.models import Note, NoteForm |
| 16 | +from jv3.models import RedactedNote ##, RedactedSkip |
| 17 | +from jv3.models import WordMap, WordMeta |
| 18 | +import jv3.utils |
| 19 | +from jv3.models import ActivityLog, UserRegistration, CouhesConsent, ChangePasswordRequest, BugReport, ServerLog, CachedActivityLogStats |
| 20 | +from jv3.utils import gen_cookie, makeChangePasswordRequest, nonblank, get_most_recent, gen_confirm_newuser_email_body, gen_confirm_change_password_email, logevent, current_time_decimal, basicauth_get_user_by_emailaddr, make_username, get_user_by_email, is_consenting_study1, is_consenting_study2, json_response, set_consenting |
| 21 | +from django.template.loader import get_template |
| 22 | +import sys,string,time,logging |
| 23 | +import tempfile,os |
| 24 | +from decimal import Decimal |
| 25 | +from jv3.views import * |
| 26 | + |
| 27 | +def _filter_dupes(note_queryset): |
| 28 | + distinct_jids = set([x[0] for x in note_queryset.values_list('jid')]) |
| 29 | + notes = {} |
| 30 | + for n in note_queryset: |
| 31 | + if n.jid in notes and notes[n.jid].id < n.id : continue |
| 32 | + notes[n.jid] = n |
| 33 | + return [notes[n.jid] for n in note_queryset] |
| 34 | + |
| 35 | +## Chrome Extension Get/Post Methods |
| 36 | +def get_json_notes(request): |
| 37 | + request_user = basicauth_get_user_by_emailaddr(request) |
| 38 | + if not request_user: |
| 39 | + logevent(request,'ActivityLog.create POST',401,jv3.utils.decode_emailaddr(request)) |
| 40 | + response = HttpResponse(JSONEncoder().encode({'autherror':"Incorrect user/password combination"}), "text/json") |
| 41 | + response.status_code = 401 |
| 42 | + return response |
| 43 | + |
| 44 | + ## Filter out notes that have already been redacted |
| 45 | + notes = _filter_dupes(request_user.note_owner.filter(deleted=0).order_by("-created")) |
| 46 | + |
| 47 | + ndicts = [ extract_zen_notes_data(note) for note in notes ] |
| 48 | + allNotes = [] |
| 49 | + for note in ndicts: |
| 50 | + allNotes.append( |
| 51 | + {"jid":note['jid'],"version":note['version'], "contents":note['noteText'], |
| 52 | + "deleted":note['deleted'], "created":str(note['created']), |
| 53 | + "edited":str(note['edited']) }) |
| 54 | + |
| 55 | + allNotes.sort(lambda x,y:cmp(x['created'], y['created']) ) |
| 56 | + |
| 57 | + |
| 58 | + response = HttpResponse(JSONEncoder().encode({"notes":allNotes}), "text/json") |
| 59 | + response.status_code = 200 |
| 60 | + return response |
| 61 | + |
| 62 | +def post_json_notes(request): |
| 63 | + return put_zen(request) |
| 64 | + |
| 65 | +def sort_user_for_notes(request_user, note_list): |
| 66 | + ## Sort and return user's notes |
| 67 | + if request_user.note_owner.filter(jid="-1").count() > 0: |
| 68 | + ## we want to determine order using magic note |
| 69 | + magic_note = request_user.note_owner.filter(jid="-1")[0] |
| 70 | + note_order = JSONDecoder().decode(magic_note.contents)['noteorder'] |
| 71 | + notes = filter(lambda x: x.jid != -1,note_list) |
| 72 | + def sort_order(nx,ny): |
| 73 | + if nx.jid in note_order and ny.jid in note_order: |
| 74 | + result = note_order.index(nx.jid) - note_order.index(ny.jid) |
| 75 | + else: |
| 76 | + result = int((ny.created - nx.created)/1000) |
| 77 | + return result |
| 78 | + ## sort 'em |
| 79 | + notes.sort(sort_order) |
| 80 | + else: |
| 81 | + # sort by creation date ? |
| 82 | + notes = filter(lambda x : x.jid != -1, django_notes) |
| 83 | + notes.sort(key=lambda x:-x.created) |
| 84 | + return notes |
| 85 | + |
| 86 | + |
| 87 | +def carefully(fn): |
| 88 | + import sys, traceback |
| 89 | + def boo(*args,**kwargs): |
| 90 | + try: |
| 91 | + return fn(*args,**kwargs) |
| 92 | + except Exception as exc: |
| 93 | + print "Unexpected error:", sys.exc_info()[0] |
| 94 | + print str(exc) |
| 95 | + traceback.print_exc(file=sys.stdout) |
| 96 | + raise |
| 97 | + return boo |
| 98 | + |
| 99 | +# @carefully |
| 100 | +def post_json_get_updates(request): |
| 101 | + request_user = basicauth_get_user_by_emailaddr(request); |
| 102 | + if not request_user: |
| 103 | + logevent(request,'ActivityLog.create POST',401,jv3.utils.decode_emailaddr(request)) |
| 104 | + response = HttpResponse(JSONEncoder().encode({'autherror':"Incorrect user/password combination"}), "text/json") |
| 105 | + response.status_code = 401; |
| 106 | + return response |
| 107 | + if not request.raw_post_data: |
| 108 | + response = HttpResponse(JSONEncoder().encode({'committed':[]}), "text/json") |
| 109 | + response.status_code = 200; |
| 110 | + return response |
| 111 | + |
| 112 | + ## 1) put_zen method of updating client's "Modified Notes" |
| 113 | + responses = [] # Successful commit of note. |
| 114 | + updateResponses = [] # Conflicting notes with new content! |
| 115 | + payload = JSONDecoder().decode(request.raw_post_data) |
| 116 | + userNotes = _filter_dupes(request_user.note_owner.all()) |
| 117 | + |
| 118 | + #print 'Process modified notes' |
| 119 | + for datum in payload['modifiedNotes']: |
| 120 | + form = NoteForm(datum) |
| 121 | + form.data['owner'] = request_user.id; |
| 122 | + matching_notes = [u for u in userNotes if u.jid==form.data['jid']] |
| 123 | + assert len(matching_notes) in [0,1], "Got two, which is fail %d " % form.data['jid'] |
| 124 | + #print '# matching notes:', len(matching_notes) |
| 125 | + if len(matching_notes) == 0: ## Save new note |
| 126 | + if form.is_valid() : |
| 127 | + new_model = form.save() |
| 128 | + responses.append({ |
| 129 | + "jid": form.data['jid'], |
| 130 | + "version": form.data['version'], |
| 131 | + "status": 201}) |
| 132 | + logevent(request, 'Note.create', 200, form.data['jid']) |
| 133 | + else: |
| 134 | + logevent(request,'Note.create',400,form.errors) |
| 135 | + responses.append({"jid": form.data['jid'], "status": 400}) |
| 136 | + else: |
| 137 | + ## UPDATE an existing note: check if the client version needs updating |
| 138 | + conflictNote = matching_notes[0] |
| 139 | + ##print "conflictNote/form Ver: ", conflictNote.version, form.data['version'] |
| 140 | + if (conflictNote.version > form.data['version']): |
| 141 | + # Server's version of note is conflicting with client's version, merge! |
| 142 | + if form.is_valid(): |
| 143 | + for key in Note.update_fields: ## key={contents,created,deleted,edited} |
| 144 | + if key == "contents": |
| 145 | + newContent = ("Two versions of this note:" + |
| 146 | + "\nSubmitted Copy:\n%s\n\nServer Copy:\n%s" |
| 147 | + % (form.data[key],conflictNote.contents)) |
| 148 | + conflictNote.__setattr__(key, newContent) |
| 149 | + else: |
| 150 | + conflictNote.__setattr__(key, form.data[key]) |
| 151 | + newVersion = max(conflictNote.version, |
| 152 | + form.data['version']) + 1 |
| 153 | + conflictNote.version = newVersion |
| 154 | + newEdited = max(conflictNote.edited, |
| 155 | + form.data['edited']) |
| 156 | + conflictNote.edited = newEdited |
| 157 | + ## Saved note will be MOST-up-to-date, ie:(max(both versions)+1) |
| 158 | + conflictNote.save() |
| 159 | + updateResponses.append({"jid": form.data['jid'], |
| 160 | + "version": newVersion, |
| 161 | + "edited": newEdited, |
| 162 | + "contents": newContent, |
| 163 | + "status": 201}) |
| 164 | + continue |
| 165 | + continue |
| 166 | + # If the data contains no errors, |
| 167 | + elif form.is_valid(): # No version conflict, update server version. |
| 168 | + #print "update server's note" |
| 169 | + for key in Note.update_fields: |
| 170 | + conflictNote.__setattr__(key, form.data[key]) |
| 171 | + newVersion = form.data['version'] + 1 |
| 172 | + conflictNote.version = newVersion |
| 173 | + conflictNote.save() |
| 174 | + responses.append({"jid": form.data['jid'], |
| 175 | + "version": newVersion, |
| 176 | + "status": 201}) |
| 177 | + else: |
| 178 | + responses.append({"jid": form.data['jid'], "status": 400}) |
| 179 | + logevent(request, 'Note.create', 400, form.errors) |
| 180 | + pass |
| 181 | + pass |
| 182 | + pass |
| 183 | + |
| 184 | + #print 'process unmodified notes' |
| 185 | + ## 2) Figure out which of Client's unmodified notes has been updated on server |
| 186 | + updateFinal = [] # Server has newer version of these notes. |
| 187 | + for jid, ver in payload['unmodifiedNotes'].items(): |
| 188 | + notes = [u for u in userNotes if u.jid==jid] |
| 189 | + if notes and notes[0].version > ver: |
| 190 | + note = extract_zen_notes_data(notes[0]) |
| 191 | + updateFinal.append({"jid": note['jid'], |
| 192 | + "version": note['version'], |
| 193 | + "created":str(note['created']), |
| 194 | + "edited": str(note['edited']), |
| 195 | + "deleted": note['deleted'], |
| 196 | + "contents": note['noteText'], |
| 197 | + "modified": 0}) |
| 198 | + pass |
| 199 | + |
| 200 | + #print 'process notes only known to server' |
| 201 | + ## 3) Return notes only server knows about! |
| 202 | + clientJIDs = map(lambda x:int(x['jid']), payload['modifiedNotes']) |
| 203 | + clientJIDs.extend(map(lambda x:int(x), payload['unmodifiedNotes'].keys())) |
| 204 | + serverNotes = [u for u in userNotes if u.deleted==0 and not u.jid in clientJIDs] |
| 205 | + |
| 206 | + serverNotes = sort_user_for_notes(request_user, serverNotes) |
| 207 | + |
| 208 | + ndicts = [ extract_zen_notes_data(note) for note in serverNotes ] |
| 209 | + |
| 210 | + ndicts.reverse() |
| 211 | + |
| 212 | + servNotes = [] # New notes server has & client doesn't. |
| 213 | + for note in ndicts: |
| 214 | + servNotes.append({ |
| 215 | + "jid": note['jid'], |
| 216 | + "version": note['version'], |
| 217 | + "contents": note['noteText'], |
| 218 | + "deleted": note['deleted'], |
| 219 | + "created": str(note['created']), |
| 220 | + "edited": str(note['edited']), |
| 221 | + "modified": 0}) |
| 222 | + pass |
| 223 | + |
| 224 | + |
| 225 | + #print 'Add magical note!' |
| 226 | + magicNote = {} |
| 227 | + magicalNote = [u for u in userNotes if u.jid=='-1'] |
| 228 | + if magicalNote: # magical note found |
| 229 | + for key, value in magicalNote.values()[0].items(): |
| 230 | + if key == 'owner_id': |
| 231 | + pass |
| 232 | + elif type(value) == Decimal: |
| 233 | + magicNote[key] = int(value) |
| 234 | + else: |
| 235 | + magicNote[key] = value |
| 236 | + pass |
| 237 | + pass |
| 238 | + |
| 239 | + response = HttpResponse( |
| 240 | + JSONEncoder().encode({ |
| 241 | + "committed": responses, |
| 242 | + "update": updateResponses, |
| 243 | + "updateFinal": updateFinal, |
| 244 | + "unknownNotes": servNotes, |
| 245 | + "magicNote": magicNote}), |
| 246 | + "text/json") |
| 247 | + |
| 248 | + response.status_code = 200; |
| 249 | + return response |
| 250 | + |
| 251 | + |
0 commit comments