<commit_before>from flask import Flask
from flask import request, abort
import json
import requests
import urllib
import requests.auth
import os
from client import TodoistClient
from uuid import uuid4
from pymongo import MongoClient
from dateutil.parser import parse
from webob import Response

FB_MESSAGES_ENDPOINT = 'https://graph.facebook.com/v2.6/me/messages'
OAUTH_CODE_ENDPOINT = 'https://todoist.com/oauth/authorize'
OAUTH_ACCESS_TOKEN_ENDPOINT = 'https://todoist.com/oauth/access_token'
REDIRECT_URI = 'http://pure-hamlet-63323.herokuapp.com/todoist_callback'
MONGO_DB_ENDPOINT = 'ds021434.mlab.com'
MONGO_DB_PORT = 21434

def connect():
    connection = MongoClient(MONGO_DB_ENDPOINT, MONGO_DB_PORT)
    handle = connection['todoist_access_tokens']
    handle.authenticate(
        os.environ['MONGO_DB_USERNAME'],
        os.environ['MONGO_DB_PWD']
    )
    return handle

app = Flask(__name__)
handle = connect()

@app.route('/webhook', methods=['GET', 'POST'])
def webhook():
    if request.method == 'GET':
        if request.args.get('hub.verify_token') == 'todoist_is_my_fave':
            return request.args.get('hub.challenge')
        else:
            return 'Wrong validation token'
    elif request.method == 'POST':
        data = json.loads(request.data)['entry'][0]['messaging']
        for event in data:
            if 'sender' in event:
                sender_id = event['sender']['id']
                if handle.access_tokens.find(
                    {'sender_id': sender_id}
                ).count() == 0:
                    get_access_token(sender_id)
                sender_id_matches = [x for x in handle.access_tokens.find(
                    {'sender_id': sender_id})]
                if sender_id_matches:
                    access_token = sender_id_matches[0]['access_token']
                    tc = TodoistClient(access_token)
                    if not 'user_id' in [x for x in handle.access_tokens.find(
                        {'access_token': access_token}
                    )][0]:
                        handle.access_tokens.update(
                            {'access_token': access_token},
                            {
                                '$set': {
                                    'user_id': tc.sync_response['user']['id']
                                }
                            }
                        )
                    if 'message' in event and 'text' in event['message']:
                        message = event['message']['text']
                        if 'tasks' in message.lower():
                            if ' in ' in message.lower():
                                project_name = message.lower().split(
                                    ' in '
                                )[1]
                                project_tasks = tc.get_project_tasks(
                                    project_name
                                )
                                if type(project_tasks) is list:
                                    if len(project_tasks) > 0:
                                        send_tasks(
                                            sender_id,
                                            project_tasks
                                        )
                                    else:
                                        send_FB_text(
                                            sender_id,
                                            'No tasks in this project.'
                                        )
                                else:
                                    send_FB_text(
                                        sender_id,
                                        'Not a valid project.'
                                    )
                            elif ' up to ' in message.lower():
                                date_string = message.lower().split(
                                    ' up to '
                                )[1]
                                datetime = None
                                try:
                                    datetime = parse(date_string)
                                except:
                                    send_FB_text(
                                        sender_id,
                                        (
                                            'Date text not recognized. \n'
                                            'Try using actual dates.'
                                        )
                                    )
                                if datetime:
                                    send_tasks(
                                        sender_id,
                                        tc.get_tasks_up_to_date(
                                            datetime.date()
                                        )
                                    )
                            else:
                                send_tasks(
                                    sender_id,
                                    tc.get_this_week_tasks()
                                )
                        elif ' due ' in message:
                            write_task(sender_id, tc, message)
                        else:
                            send_generic_response(sender_id)
                    if 'postback' in event:
                        payload = event['postback']['payload']
                        print 'Payload: {0}'.format(payload)
                        if payload == 'tasks':
                            send_tasks(sender_id, tc.get_this_week_tasks())
                        if payload == 'write':
                            send_write_request(sender_id)
                        if 'task_id' in payload:
                            complete_task(
                                sender_id,
                                tc,
                                payload.split(':')[1]
                            )
        return Response()

def send_tasks(sender_id, tasks):
    for task in tasks:
        send_FB_buttons(
            sender_id,
            '* {0} (Due {1})'.format(
                task['content'],
                task['date_string'] if task['date_string'] else 'never'
            ),
            [
                {
                    'type': 'postback',
                    'title':
                    'Complete',
                    'payload': '{0}:{1}'.format(
                        'task_id',
                        task['id']
                    )
                },
            ]
        )

def send_write_request(sender_id):
    send_FB_text(
        sender_id,
        (
            'Enter your task as follows: '
            '<Task Name> due <Date string>. '
            'Enter \'never\' if there is no due date.'
        )
    )

def write_task(sender_id, tc, message):
    task_name = message.split(' due ')[0]
    date_string = message.split(' due ')[1]
    if date_string == 'never':
        tc.write_task(
            task_name,
            'Inbox',
        )
    else:
        tc.write_task(
            task_name,
            'Inbox',
            date_string=date_string
        )
    send_FB_text(sender_id, 'Task written.')

def complete_task(sender_id, tc, task_id):
    tc.complete_task(task_id)
    send_FB_text(sender_id, 'Task completed.')

def send_generic_response(sender_id):
    send_FB_buttons(
        sender_id,
        (
            'Hi there! Would you like view your tasks or write tasks?\n'
            'You can also view tasks by typing \'tasks\'.\n'
            'You can view tasks up to a certain date'
            ' by typing \'tasks up to <date_string>\'\n'
            'You can view tasks in a specific project'
            ' by typing \'tasks in <project_name>\'\n'
        ),
        [
            {
                'type': 'postback',
                'title': 'View my tasks',
                'payload': 'tasks'
            },
            {
                'type': 'postback',
                'title': 'Write tasks',
                'payload': 'write'
            }
        ],
    )

@app.route('/todoist_callback')
def todoist_callback(methods=['GET']):
    if request.method == 'GET':
        error = request.args.get('error', '')
        if error:
            return 'Error: ' + error
        state = request.args.get('state', '')
        code = request.args.get('code')
        # We'll change this next line in just a moment
        access_token = get_token(code)
        print 'Access token: {0}'.format(access_token)
        handle.access_tokens.update(
            {'access_token': 'temp'},
            {
                '$set': {
                    'access_token': access_token
                }
            }
        )
        return 'success' if access_token and \
            handle.access_tokens.find(
                {'access_token': access_token}
            ).count() else 'failure'

def get_access_token(sender_id):
    handle.access_tokens.insert(
        {
            'sender_id': sender_id,
            'access_token': 'temp'
        }
    )
    send_FB_buttons(
        sender_id,
        'Looks like you haven\'t authorized Todoist.',
        [
            {
                'type': 'web_url',
                'title': 'Authorize now',
                'url': '{0}?{1}'.format(
                    OAUTH_CODE_ENDPOINT,
                    urllib.urlencode(
                        {
                            'client_id': os.environ['TODOIST_CLIENT_ID'],
                            'scope': 'data:read_write,data:delete',
                            'redirect_uri': REDIRECT_URI,
                            'state': uuid4()
                        }
                    )
                )
            }
        ],
    )

def get_token(code):
    post_data = {
        'client_id': os.environ['TODOIST_CLIENT_ID'],
        'client_secret': os.environ['TODOIST_CLIENT_SECRET'],
        'code': code,
        'redirect_uri': REDIRECT_URI
    }
    response = requests.post(
        OAUTH_ACCESS_TOKEN_ENDPOINT,
        data=post_data
    )
    token_json = response.json()
    return token_json['access_token']

@app.route('/todoist_notifications', methods=['POST'])
def todoist_notifications():
    if request.method == 'POST':
        data = json.loads(request.data)
        print data
        if data['event_name'] == 'item:added':
            user_id = data['event_data']['user_id']
            sender_id = [x for x in handle.access_tokens.find(
                {'user_id': user_id})][0]['sender_id']
            send_FB_text(sender_id, 'A task was just added.')
        return Response()

def send_FB_message(sender_id, message):
    fb_response = requests.post(
        FB_MESSAGES_ENDPOINT,
        params={'access_token': os.environ['FB_APP_TOKEN']},
        data=json.dumps(
            {
                'recipient': {
                    'id': sender_id
                },
                'message': message
            }
        ),
        headers={'content-type': 'application/json'})
    if not fb_response.ok:
        print 'Not OK: {0}: {1}'.format(
            fb_response.status_code,
            fb_response.text
        )
    else:
        print 'OK: {0}'.format(200)

def send_FB_text(sender_id, text):
    return send_FB_message(
        sender_id,
        {
            'text': text
        }
    )

def send_FB_buttons(sender_id, text, buttons):
    return send_FB_message(
        sender_id,
        {
            'attachment': {
                'type': 'template',
                'payload': {
                    'template_type': 'button',
                    'text': text,
                    'buttons': buttons
                }
            }
        }
    )

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
<commit_msg>notifications<commit_after>from flask import Flask
from flask import request, abort
import json
import requests
import urllib
import requests.auth
import os
from client import TodoistClient
from uuid import uuid4
from pymongo import MongoClient
from dateutil.parser import parse
from webob import Response

FB_MESSAGES_ENDPOINT = 'https://graph.facebook.com/v2.6/me/messages'
OAUTH_CODE_ENDPOINT = 'https://todoist.com/oauth/authorize'
OAUTH_ACCESS_TOKEN_ENDPOINT = 'https://todoist.com/oauth/access_token'
REDIRECT_URI = 'http://pure-hamlet-63323.herokuapp.com/todoist_callback'
MONGO_DB_ENDPOINT = 'ds021434.mlab.com'
MONGO_DB_PORT = 21434

def connect():
    connection = MongoClient(MONGO_DB_ENDPOINT, MONGO_DB_PORT)
    handle = connection['todoist_access_tokens']
    handle.authenticate(
        os.environ['MONGO_DB_USERNAME'],
        os.environ['MONGO_DB_PWD']
    )
    return handle

app = Flask(__name__)
handle = connect()

@app.route('/webhook', methods=['GET', 'POST'])
def webhook():
    if request.method == 'GET':
        if request.args.get('hub.verify_token') == 'todoist_is_my_fave':
            return request.args.get('hub.challenge')
        else:
            return 'Wrong validation token'
    elif request.method == 'POST':
        data = json.loads(request.data)['entry'][0]['messaging']
        for event in data:
            if 'sender' in event:
                sender_id = event['sender']['id']
                if handle.access_tokens.find(
                    {'sender_id': sender_id}
                ).count() == 0:
                    get_access_token(sender_id)
                sender_id_matches = [x for x in handle.access_tokens.find(
                    {'sender_id': sender_id})]
                if sender_id_matches:
                    access_token = sender_id_matches[0]['access_token']
                    tc = TodoistClient(access_token)
                    if not 'user_id' in [x for x in handle.access_tokens.find(
                        {'access_token': access_token}
                    )][0]:
                        handle.access_tokens.update(
                            {'access_token': access_token},
                            {
                                '$set': {
                                    'user_id': tc.sync_response['user']['id']
                                }
                            }
                        )
                    if 'message' in event and 'text' in event['message']:
                        message = event['message']['text']
                        if 'tasks' in message.lower():
                            if ' in ' in message.lower():
                                project_name = message.lower().split(
                                    ' in '
                                )[1]
                                project_tasks = tc.get_project_tasks(
                                    project_name
                                )
                                if type(project_tasks) is list:
                                    if len(project_tasks) > 0:
                                        send_tasks(
                                            sender_id,
                                            project_tasks
                                        )
                                    else:
                                        send_FB_text(
                                            sender_id,
                                            'No tasks in this project.'
                                        )
                                else:
                                    send_FB_text(
                                        sender_id,
                                        'Not a valid project.'
                                    )
                            elif ' up to ' in message.lower():
                                date_string = message.lower().split(
                                    ' up to '
                                )[1]
                                datetime = None
                                try:
                                    datetime = parse(date_string)
                                except:
                                    send_FB_text(
                                        sender_id,
                                        (
                                            'Date text not recognized. \n'
                                            'Try using actual dates.'
                                        )
                                    )
                                if datetime:
                                    send_tasks(
                                        sender_id,
                                        tc.get_tasks_up_to_date(
                                            datetime.date()
                                        )
                                    )
                            else:
                                send_tasks(
                                    sender_id,
                                    tc.get_this_week_tasks()
                                )
                        elif ' due ' in message:
                            write_task(sender_id, tc, message)
                        else:
                            send_generic_response(sender_id)
                    if 'postback' in event:
                        payload = event['postback']['payload']
                        print 'Payload: {0}'.format(payload)
                        if payload == 'tasks':
                            send_tasks(sender_id, tc.get_this_week_tasks())
                        if payload == 'write':
                            send_write_request(sender_id)
                        if 'task_id' in payload:
                            complete_task(
                                sender_id,
                                tc,
                                payload.split(':')[1]
                            )
        return Response()

def send_tasks(sender_id, tasks):
    for task in tasks:
        send_FB_buttons(
            sender_id,
            '* {0} (Due {1})'.format(
                task['content'],
                task['date_string'] if task['date_string'] else 'never'
            ),
            [
                {
                    'type': 'postback',
                    'title':
                    'Complete',
                    'payload': '{0}:{1}'.format(
                        'task_id',
                        task['id']
                    )
                },
            ]
        )

def send_write_request(sender_id):
    send_FB_text(
        sender_id,
        (
            'Enter your task as follows: '
            '<Task Name> due <Date string>. '
            'Enter \'never\' if there is no due date.'
        )
    )

def write_task(sender_id, tc, message):
    task_name = message.split(' due ')[0]
    date_string = message.split(' due ')[1]
    if date_string == 'never':
        tc.write_task(
            task_name,
            'Inbox',
        )
    else:
        tc.write_task(
            task_name,
            'Inbox',
            date_string=date_string
        )
    send_FB_text(sender_id, 'Task written.')

def complete_task(sender_id, tc, task_id):
    tc.complete_task(task_id)
    send_FB_text(sender_id, 'Task completed.')

def send_generic_response(sender_id):
    send_FB_buttons(
        sender_id,
        (
            'Hi there! Would you like view your tasks or write tasks?\n'
            'You can also view tasks by typing \'tasks\'.\n'
            'You can view tasks up to a certain date'
            ' by typing \'tasks up to <date_string>\'\n'
            'You can view tasks in a specific project'
            ' by typing \'tasks in <project_name>\'\n'
        ),
        [
            {
                'type': 'postback',
                'title': 'View my tasks',
                'payload': 'tasks'
            },
            {
                'type': 'postback',
                'title': 'Write tasks',
                'payload': 'write'
            }
        ],
    )

@app.route('/todoist_callback')
def todoist_callback(methods=['GET']):
    if request.method == 'GET':
        error = request.args.get('error', '')
        if error:
            return 'Error: ' + error
        state = request.args.get('state', '')
        code = request.args.get('code')
        # We'll change this next line in just a moment
        access_token = get_token(code)
        print 'Access token: {0}'.format(access_token)
        handle.access_tokens.update(
            {'access_token': 'temp'},
            {
                '$set': {
                    'access_token': access_token
                }
            }
        )
        return 'success' if access_token and \
            handle.access_tokens.find(
                {'access_token': access_token}
            ).count() else 'failure'

def get_access_token(sender_id):
    handle.access_tokens.insert(
        {
            'sender_id': sender_id,
            'access_token': 'temp'
        }
    )
    send_FB_buttons(
        sender_id,
        'Looks like you haven\'t authorized Todoist.',
        [
            {
                'type': 'web_url',
                'title': 'Authorize now',
                'url': '{0}?{1}'.format(
                    OAUTH_CODE_ENDPOINT,
                    urllib.urlencode(
                        {
                            'client_id': os.environ['TODOIST_CLIENT_ID'],
                            'scope': 'data:read_write,data:delete',
                            'redirect_uri': REDIRECT_URI,
                            'state': uuid4()
                        }
                    )
                )
            }
        ],
    )

def get_token(code):
    post_data = {
        'client_id': os.environ['TODOIST_CLIENT_ID'],
        'client_secret': os.environ['TODOIST_CLIENT_SECRET'],
        'code': code,
        'redirect_uri': REDIRECT_URI
    }
    response = requests.post(
        OAUTH_ACCESS_TOKEN_ENDPOINT,
        data=post_data
    )
    token_json = response.json()
    return token_json['access_token']

@app.route('/todoist_notifications', methods=['POST'])
def todoist_notifications():
    if request.method == 'POST':
        data = json.loads(request.data)
        if data['event_name'] == 'item:added':
            user_id = data['event_data']['user_id']
            sender_id = [x for x in handle.access_tokens.find(
                {'user_id': user_id})][0]['sender_id']
            send_FB_text(sender_id, 'A task was just added.')
        if data['event_name'] == 'item:completed':
            user_id = data['event_data']['user_id']
            sender_id = [x for x in handle.access_tokens.find(
                {'user_id': user_id})][0]['sender_id']
            send_FB_text(sender_id, 'A task was just completed.')
        return Response()

def send_FB_message(sender_id, message):
    fb_response = requests.post(
        FB_MESSAGES_ENDPOINT,
        params={'access_token': os.environ['FB_APP_TOKEN']},
        data=json.dumps(
            {
                'recipient': {
                    'id': sender_id
                },
                'message': message
            }
        ),
        headers={'content-type': 'application/json'})
    if not fb_response.ok:
        print 'Not OK: {0}: {1}'.format(
            fb_response.status_code,
            fb_response.text
        )
    else:
        print 'OK: {0}'.format(200)

def send_FB_text(sender_id, text):
    return send_FB_message(
        sender_id,
        {
            'text': text
        }
    )

def send_FB_buttons(sender_id, text, buttons):
    return send_FB_message(
        sender_id,
        {
            'attachment': {
                'type': 'template',
                'payload': {
                    'template_type': 'button',
                    'text': text,
                    'buttons': buttons
                }
            }
        }
    )

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
<|endoftext|>


<commit_before>from flask import Flask
from flask import request, abort
import json
import requests
import urllib
import requests.auth
import os
from client import TodoistClient
from uuid import uuid4

app = Flask(__name__)

FB_MESSAGES_ENDPOINT = "https://graph.facebook.com/v2.6/me/messages"
OAUTH_CODE_ENDPOINT = "https://todoist.com/oauth/authorize"
OAUTH_ACCESS_TOKEN_ENDPOINT = "https://todoist.com/oauth/access_token"
REDIRECT_URI = "http://pure-hamlet-63323.herokuapp.com/todoist_callback"

my_sender_id = None

@app.route('/webhook', methods=['GET', 'POST'])
def webhook():
    if request.method == "GET":
        if request.args.get('hub.verify_token') == 'todoist_is_my_fave':
            return request.args.get('hub.challenge')
        else:
            return 'Wrong validation token'
    else:
        if request.method == 'POST':
            if not('TODOIST_ACCESS_TOKENS' in os.environ):
                os.environ['TODOIST_ACCESS_TOKENS'] = dict()
            data = json.loads(request.data)['entry'][0]['messaging']
            for m in data:
                print m
                if ('message' in m) and ('text' in m['message']):
                    sender_id = m['sender']['id']
                    message = m['message']['text']
                    if not(sender_id in os.environ['TODOIST_ACCESS_TOKENS']):
                        get_access_token(sender_id)
                    print os.environ['TODOIST_ACCESS_TOKENS']
                    if sender_id in os.environ['TODOIST_ACCESS_TOKENS']:
                        bot_responses = get_bot_responses(sender_id, message)
                        for bot_response in bot_responses:
                            print bot_response
                            send_FB_text(sender_id, bot_response)
        return "OK", 200

def get_bot_responses(sender_id, message):
    tc = TodoistClient(os.environ['TODOIST_ACCESS_TOKENS'])
    if message.lower() == 'tasks':
        return ['* {0} (Due {1})'.format(
            task['content'],
            task['date_string']) for task in tc.get_this_week_tasks()]
    elif 'write task' in message:
        task_name = message.split('\"')[1]
        date_string = message.split('\"')[3]
        tc.write_task(task_name, 'Inbox', date_string=date_string)
        return ['Task written.']
    else:
        return ['Type \'tasks\' to get your tasks for the next week. \
        Type \'write task \"<task_name>\" due \"<date_string>\"\'']

def get_access_token(sender_id):
    my_sender_id = sender_id
    send_FB_button(
        sender_id,
        'Looks like you haven\'t authorized Todoist.',
        'Authorize now',
        '{0}?{1}'.format(
            OAUTH_CODE_ENDPOINT,
            urllib.urlencode(
                {
                    'client_id': os.environ['TODOIST_CLIENT_ID'],
                    'scope': 'data:read_write,data:delete',
                    'redirect_uri': REDIRECT_URI,
                    'state': uuid4()
                }
            )
        )
    )

@app.route('/todoist_callback')
def todoist_callback(methods=['GET']):
    if request.method == 'GET':
        error = request.args.get('error', '')
        if error:
            return "Error: " + error
        state = request.args.get('state', '')
        code = request.args.get('code')
        # We'll change this next line in just a moment
        TODOIST_ACCESS_TOKEN = get_token(code)
        os.environ['TODOIST_ACCESS_TOKENS'][my_sender_id] = \
            TODOIST_ACCESS_TOKEN
        return "success" if TODOIST_ACCESS_TOKEN else "failure"

def get_token(code):
    post_data = {
        'client_id': os.environ['TODOIST_CLIENT_ID'],
        'client_secret': os.environ['TODOIST_CLIENT_SECRET'],
        'code': code,
        'redirect_uri': REDIRECT_URI
    }
    response = requests.post(
        OAUTH_ACCESS_TOKEN_ENDPOINT,
        data=post_data
    )
    token_json = response.json()
    return token_json["access_token"]

def send_FB_message(sender_id, message):
    fb_response = requests.post(
        FB_MESSAGES_ENDPOINT,
        params={'access_token': os.environ['FB_APP_TOKEN']},
        data=json.dumps(
            {
                'recipient': {
                    'id': sender_id
                },
                'message': message
            }
        ),
        headers={'content-type': 'application/json'})
    if not fb_response.ok:
        print "Not OK: {0}: {1}".format(
            fb_response.status_code,
            fb_response.text
        )
    else:
        print "OK: {0}".format(200)

def send_FB_text(sender_id, text):
    return send_FB_message(
        sender_id,
        {
            'text': text
        }
    )

def send_FB_button(sender_id, text, button_text, web_url):
    return send_FB_message(
        sender_id,
        {
            'attachment': {
                'type': 'template',
                'payload': {
                    'template_type': 'button',
                    'text': text,
                    'buttons': [
                        {
                            'type': 'web_url',
                            'title': button_text,
                            'url': web_url
                        }
                    ]
                }
            }
        }
    )

def updateDict(old_dict, update):
    new_dict = old_dict
    new_dict.update(update)
    return new_dict

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
<commit_msg>store access tokens<commit_after>from flask import Flask
from flask import request, abort
import json
import requests
import urllib
import requests.auth
import os
from client import TodoistClient
from uuid import uuid4

app = Flask(__name__)

FB_MESSAGES_ENDPOINT = "https://graph.facebook.com/v2.6/me/messages"
OAUTH_CODE_ENDPOINT = "https://todoist.com/oauth/authorize"
OAUTH_ACCESS_TOKEN_ENDPOINT = "https://todoist.com/oauth/access_token"
REDIRECT_URI = "http://pure-hamlet-63323.herokuapp.com/todoist_callback"

my_sender_id = None

@app.route('/webhook', methods=['GET', 'POST'])
def webhook():
    if request.method == "GET":
        if request.args.get('hub.verify_token') == 'todoist_is_my_fave':
            return request.args.get('hub.challenge')
        else:
            return 'Wrong validation token'
    else:
        if request.method == 'POST':
            data = json.loads(request.data)['entry'][0]['messaging']
            for m in data:
                print m
                if ('message' in m) and ('text' in m['message']):
                    sender_id = m['sender']['id']
                    message = m['message']['text']
                    if not(sender_id in os.environ):
                        get_access_token(sender_id)
                    if sender_id in os.environ:
                        bot_responses = get_bot_responses(sender_id, message)
                        for bot_response in bot_responses:
                            print bot_response
                            send_FB_text(sender_id, bot_response)
        return "OK", 200

def get_bot_responses(sender_id, message):
    tc = TodoistClient(os.environ[sender_id])
    if message.lower() == 'tasks':
        return ['* {0} (Due {1})'.format(
            task['content'],
            task['date_string']) for task in tc.get_this_week_tasks()]
    elif 'write task' in message:
        task_name = message.split('\"')[1]
        date_string = message.split('\"')[3]
        tc.write_task(task_name, 'Inbox', date_string=date_string)
        return ['Task written.']
    else:
        return ['Type \'tasks\' to get your tasks for the next week. \
        Type \'write task \"<task_name>\" due \"<date_string>\"\'']

def get_access_token(sender_id):
    my_sender_id = sender_id
    send_FB_button(
        sender_id,
        'Looks like you haven\'t authorized Todoist.',
        'Authorize now',
        '{0}?{1}'.format(
            OAUTH_CODE_ENDPOINT,
            urllib.urlencode(
                {
                    'client_id': os.environ['TODOIST_CLIENT_ID'],
                    'scope': 'data:read_write,data:delete',
                    'redirect_uri': REDIRECT_URI,
                    'state': uuid4()
                }
            )
        )
    )

@app.route('/todoist_callback')
def todoist_callback(methods=['GET']):
    if request.method == 'GET':
        error = request.args.get('error', '')
        if error:
            return "Error: " + error
        state = request.args.get('state', '')
        code = request.args.get('code')
        # We'll change this next line in just a moment
        access_token = get_token(code)
        os.environ[my_sender_id] = \
            access_token
        return "success" if access_token else "failure"

def get_token(code):
    post_data = {
        'client_id': os.environ['TODOIST_CLIENT_ID'],
        'client_secret': os.environ['TODOIST_CLIENT_SECRET'],
        'code': code,
        'redirect_uri': REDIRECT_URI
    }
    response = requests.post(
        OAUTH_ACCESS_TOKEN_ENDPOINT,
        data=post_data
    )
    token_json = response.json()
    return token_json["access_token"]

def send_FB_message(sender_id, message):
    fb_response = requests.post(
        FB_MESSAGES_ENDPOINT,
        params={'access_token': os.environ['FB_APP_TOKEN']},
        data=json.dumps(
            {
                'recipient': {
                    'id': sender_id
                },
                'message': message
            }
        ),
        headers={'content-type': 'application/json'})
    if not fb_response.ok:
        print "Not OK: {0}: {1}".format(
            fb_response.status_code,
            fb_response.text
        )
    else:
        print "OK: {0}".format(200)

def send_FB_text(sender_id, text):
    return send_FB_message(
        sender_id,
        {
            'text': text
        }
    )

def send_FB_button(sender_id, text, button_text, web_url):
    return send_FB_message(
        sender_id,
        {
            'attachment': {
                'type': 'template',
                'payload': {
                    'template_type': 'button',
                    'text': text,
                    'buttons': [
                        {
                            'type': 'web_url',
                            'title': button_text,
                            'url': web_url
                        }
                    ]
                }
            }
        }
    )

def updateDict(old_dict, update):
    new_dict = old_dict
    new_dict.update(update)
    return new_dict

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
<|endoftext|>


<commit_before>from time import time
import binascii
import hashlib
import json
from contextlib import contextmanager
from datetime import datetime
from json import JSONEncoder
from uuid import UUID
import shortuuid

class Event:
    """
    Event handlers storage.

    Example:

    >>> def handler(**kwargs):
    ...     print("Triggered:", kwargs)
    ...
    >>> Event.get('some_thing_happened').attach(handler)
    >>> Event.get('some_thing_happened').emit(status='OK')
    Triggered: {'status': 'OK'}

    """

    _repo = {}

    def __init__(self, name):
        self.handlers = []
        self.name = name

    def attach(self, handler, priority=0):
        handler._event_priority = priority
        self.handlers.append(handler)
        self.handlers.sort(key=lambda h: h._event_priority)

    def detach(self, handler):
        self.handlers.remove(handler)

    @classmethod
    def get(cls, name):
        instance = cls._repo.get(name, None)
        if not instance:
            instance = cls(name)
            cls._repo[name] = instance
        return instance

    @contextmanager
    def subscribe(self, *handlers):
        for handler in handlers:
            self.attach(handlers)
        try:
            yield
        finally:
            for handlers in handlers:
                self.detach(handlers)

    def emit(self, *args, **kwargs):
        for handler in self.handlers:
            handler(*args, **kwargs)

def on(event_name, priority=0):
    def decorator(fn):
        Event.get(event_name).attach(fn, priority)
    return decorator

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        self.__dict__ = self

class VersatileEncoder(JSONEncoder):
    """
    Encode all "difficult" object such as UUID
    """

    def _iterencode(self, obj, markers=None):

        if isinstance(obj, tuple) and hasattr(obj, '_asdict'):
            gen = self._iterencode_dict(obj._asdict(), markers)
        else:
            gen = JSONEncoder._iterencode(self, obj, markers)
        for chunk in gen:
            yield chunk

    def default(self, obj):
        if isinstance(obj, UUID):
            return str(obj)
        elif isinstance(obj, datetime):
            return str(obj)
        else:
            try:
                return JSONEncoder.default(self, obj)
            except TypeError:
                return str(obj)

def to_json(obj):
    return json.dumps(obj, ensure_ascii=False, cls=VersatileEncoder).replace("</", "<\\/")

def from_json(s):
    return AttrDict(json.loads(s))

def make_rand(length=16):
    return shortuuid.ShortUUID().random(length)

def make_hash(secret, as_binary=False, **kwargs):
    kwargs = kwargs or dict(
        salt=b'tokit',
        iterations=82016,
    )
    # might be GIL dependent
    dk = hashlib.pbkdf2_hmac('sha512', str.encode(secret), **kwargs)
    if as_binary:
        return dk
    else:
        return binascii.hexlify(dk).decode()

try:
    import markdown

    def md_html(raw):
        """
        Convert Markdown format string to HTML
        WARNING: Be careful of leading spaces
        """
        html = markdown.markdown(raw.decode())
        return html
except:
    pass

class cached_property(object):
    """
    A property that is only computed once per instance and then replaces itself
    with an ordinary attribute. Deleting the attribute resets the property.
    Source: https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76
    """  # noqa

    def __init__(self, func):
        self.__doc__ = getattr(func, '__doc__')
        self.func = func

    def __get__(self, obj, cls):
        if obj is None:
            return self
        value = obj.__dict__[self.func.__name__] = self.func(obj)
        return value

class cached_property_ttl(object):
    """
    A property that is only computed once per instance and then replaces itself
    with an ordinary attribute. Setting the ttl to a number expresses how long
    the property will last before being timed out.
    Source: https://github.com/pydanny/cached-property/blob/master/cached_property.py
    """

    def __init__(self, ttl=None):
        if callable(ttl):
            func = ttl
            ttl = None
        else:
            func = None
        self.ttl = ttl
        self._prepare_func(func)

    def __call__(self, func):
        self._prepare_func(func)
        return self

    def __get__(self, obj, cls):
        if obj is None:
            return self

        now = time()
        obj_dict = obj.__dict__
        name = self.__name__
        try:
            value, last_updated = obj_dict[name]
        except KeyError:
            pass
        else:
            ttl_expired = self.ttl and self.ttl < now - last_updated
            if not ttl_expired:
                return value

        value = self.func(obj)
        obj_dict[name] = (value, now)
        return value

    def __delete__(self, obj):
        obj.__dict__.pop(self.__name__, None)

    def __set__(self, obj, value):
        obj.__dict__[self.__name__] = (value, time())

    def _prepare_func(self, func):
        self.func = func
        if func:
            self.__doc__ = func.__doc__
            self.__name__ = func.__name__
            self.__module__ = func.__module__
<commit_msg>Add md5<commit_after>from time import time
import binascii
import hashlib
import json
from contextlib import contextmanager
from datetime import datetime
from json import JSONEncoder
from uuid import UUID
import shortuuid

class Event:
    """
    Event handlers storage.

    Example:

    >>> def handler(**kwargs):
    ...     print("Triggered:", kwargs)
    ...
    >>> Event.get('some_thing_happened').attach(handler)
    >>> Event.get('some_thing_happened').emit(status='OK')
    Triggered: {'status': 'OK'}

    """

    _repo = {}

    def __init__(self, name):
        self.handlers = []
        self.name = name

    def attach(self, handler, priority=0):
        handler._event_priority = priority
        self.handlers.append(handler)
        self.handlers.sort(key=lambda h: h._event_priority)

    def detach(self, handler):
        self.handlers.remove(handler)

    @classmethod
    def get(cls, name):
        instance = cls._repo.get(name, None)
        if not instance:
            instance = cls(name)
            cls._repo[name] = instance
        return instance

    @contextmanager
    def subscribe(self, *handlers):
        for handler in handlers:
            self.attach(handlers)
        try:
            yield
        finally:
            for handlers in handlers:
                self.detach(handlers)

    def emit(self, *args, **kwargs):
        for handler in self.handlers:
            handler(*args, **kwargs)

def on(event_name, priority=0):
    def decorator(fn):
        Event.get(event_name).attach(fn, priority)
    return decorator

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        self.__dict__ = self

class VersatileEncoder(JSONEncoder):
    """
    Encode all "difficult" object such as UUID
    """

    def _iterencode(self, obj, markers=None):

        if isinstance(obj, tuple) and hasattr(obj, '_asdict'):
            gen = self._iterencode_dict(obj._asdict(), markers)
        else:
            gen = JSONEncoder._iterencode(self, obj, markers)
        for chunk in gen:
            yield chunk

    def default(self, obj):
        if isinstance(obj, UUID):
            return str(obj)
        elif isinstance(obj, datetime):
            return str(obj)
        else:
            try:
                return JSONEncoder.default(self, obj)
            except TypeError:
                return str(obj)

def to_json(obj):
    return json.dumps(obj, ensure_ascii=False, cls=VersatileEncoder).replace("</", "<\\/")

def from_json(s):
    return AttrDict(json.loads(s))

def make_rand(length=16):
    return shortuuid.ShortUUID().random(length)

def make_hash(secret, as_binary=False, **kwargs):
    kwargs = kwargs or dict(
        salt=b'tokit',
        iterations=82016,
    )
    # might be GIL dependent
    dk = hashlib.pbkdf2_hmac('sha512', str.encode(secret), **kwargs)
    if as_binary:
        return dk
    else:
        return binascii.hexlify(dk).decode()

def md5(text):
    m = hashlib.md5()
    m.update(str.encode(text))
    return binascii.hexlify(m.digest()).decode()

try:
    import markdown

    def md_html(raw):
        """
        Convert Markdown format string to HTML
        WARNING: Be careful of leading spaces
        """
        html = markdown.markdown(raw.decode())
        return html
except:
    pass

class cached_property(object):
    """
    A property that is only computed once per instance and then replaces itself
    with an ordinary attribute. Deleting the attribute resets the property.
    Source: https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76
    """  # noqa

    def __init__(self, func):
        self.__doc__ = getattr(func, '__doc__')
        self.func = func

    def __get__(self, obj, cls):
        if obj is None:
            return self
        value = obj.__dict__[self.func.__name__] = self.func(obj)
        return value

class cached_property_ttl(object):
    """
    A property that is only computed once per instance and then replaces itself
    with an ordinary attribute. Setting the ttl to a number expresses how long
    the property will last before being timed out.
    Source: https://github.com/pydanny/cached-property/blob/master/cached_property.py
    """

    def __init__(self, ttl=None):
        if callable(ttl):
            func = ttl
            ttl = None
        else:
            func = None
        self.ttl = ttl
        self._prepare_func(func)

    def __call__(self, func):
        self._prepare_func(func)
        return self

    def __get__(self, obj, cls):
        if obj is None:
            return self

        now = time()
        obj_dict = obj.__dict__
        name = self.__name__
        try:
            value, last_updated = obj_dict[name]
        except KeyError:
            pass
        else:
            ttl_expired = self.ttl and self.ttl < now - last_updated
            if not ttl_expired:
                return value

        value = self.func(obj)
        obj_dict[name] = (value, now)
        return value

    def __delete__(self, obj):
        obj.__dict__.pop(self.__name__, None)

    def __set__(self, obj, value):
        obj.__dict__[self.__name__] = (value, time())

    def _prepare_func(self, func):
        self.func = func
        if func:
            self.__doc__ = func.__doc__
            self.__name__ = func.__name__
            self.__module__ = func.__module__
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import absolute_import
from __future__ import print_function
from __future__ import division
from __future__ import unicode_literals

import sublime
import subprocess
import os

if(int(sublime.version()) < 3000):
    import DeviotMessages
else:
    from . import DeviotMessages

class CommandsPy(object):
    """Platformio commands

    Class to handle the different functions allowed by
    the platformio API, to know more information visit
    the web site: www.platformio.org
    """

    def __init__(self, env_path=False, console=False):
        super(CommandsPy, self).__init__()
        self.error_running = False
        self.message_queue = DeviotMessages.MessageQueue(console)
        self.message_queue.startPrint()

        # Set the enviroment Path
        if(env_path):
            os.environ['PATH'] = os.environ['PATH'] + os.pathsep + env_path

    def runCommand(self, commands, cwd=None, setReturn=False, verbose=False):

        if(not commands):
            return False

        options = commands[0]

        try:
            args = commands[1]
        except:
            args = ''

        command = "platformio -f -c sublimetext %s %s" % (options, args)

        process = subprocess.Popen(command, stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE, cwd=cwd,
                                   universal_newlines=True, shell=True)

        output = process.communicate()

        stdout = output[0]
        stderr = output[1]

        return_code = process.returncode

        if(verbose):
            self.message_queue.put(stdout)

            if(stderr):
                self.message_queue.put(stderr)

        if(return_code != 0):
            self.error_running = True

        if(setReturn):
            return stdout
<commit_msg>minor change in env path<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import absolute_import
from __future__ import print_function
from __future__ import division
from __future__ import unicode_literals

import sublime
import subprocess
import os

if(int(sublime.version()) < 3000):
    import DeviotMessages
else:
    from . import DeviotMessages

class CommandsPy(object):
    """Platformio commands

    Class to handle the different functions allowed by
    the platformio API, to know more information visit
    the web site: www.platformio.org
    """

    def __init__(self, env_path=False, console=False):
        super(CommandsPy, self).__init__()
        self.error_running = False
        self.message_queue = DeviotMessages.MessageQueue(console)
        self.message_queue.startPrint()

        # Set the enviroment Path
        if(env_path):
            os.environ['PATH'] = env_path + os.pathsep + os.environ['PATH']

    def runCommand(self, commands, cwd=None, setReturn=False, verbose=False):

        if(not commands):
            return False

        options = commands[0]

        try:
            args = commands[1]
        except:
            args = ''

        command = "platformio -f -c sublimetext %s %s" % (options, args)

        process = subprocess.Popen(command, stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE, cwd=cwd,
                                   universal_newlines=True, shell=True)

        output = process.communicate()

        stdout = output[0]
        stderr = output[1]

        return_code = process.returncode

        if(verbose):
            self.message_queue.put(stdout)

            if(stderr):
                self.message_queue.put(stderr)

        if(return_code != 0):
            self.error_running = True

        if(setReturn):
            return stdout
<|endoftext|>


<commit_before>import os
import io
import sh
import sys
import re
from multiprocessing import Pool, cpu_count
from rdftools.tools.base import RdfTool
from rdftools.tools.raptor import RaptorRdf

__author__ = 'basca'

_CLASSPATH = os.path.join(os.path.split(__file__)[0], 'classpath')

def gen_uni(num_unis, idx, seed):
    lubm = Lubm()
    lubm.generate(num_unis, idx, seed)

class Lubm(RdfTool):
    def __init__(self, ontology=None, *args, **kwargs):
        super(Lubm, self).__init__(*args, **kwargs)
        global _CLASSPATH
        self._classpath = _CLASSPATH
        self._ontology = ontology if ontology else 'http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl'

    @property
    def ontology(self):
        """
        the lubm ontology, used internally by the generator
        :return: the lubm ontology
        """
        return self._ontology

    @property
    def env_var(self):
        """
        the lubm environment variable
        :return: the lubm environment variable
        """
        return 'LUBM_PATH'

    @property
    def classpath(self):
        """
        the classpath of the bundled lubm, if the 'LUBM_PATH' env var is set, the path is taken from there
        :return: the classpath
        """
        cp = self._classpath
        if self.env_var in os.environ:
            cp = os.environ.get(self.env_var, None)
        return cp if cp else self._classpath

    def generate(self, num_universities, index, generator_seed):
        """
        the method calls the bundled lubm generator with the specified parameters

        :param num_universities: number of universities to generate
        :param index: the index at whitch the generation process starts
        :param generator_seed:  a seed used by the generator
        """
        self._log.info('[generate] Lubm num_unis={0}, index={1}'.format(num_universities, index))
        sys.stdout.flush()
        output = sh.java('-cp', self.classpath, 'edu.lehigh.swat.bench.uba.Generator', '-univ', num_universities,
                         '-index', index,
                         '-seed', generator_seed, '-onto', self.ontology)
        if output.exit_code:
            self._log.error('an error occured, while running lubm, output: \n{0}'.format(output))

    def _run(self, num_universities, index=0, generator_seed=0):
        """
        a paralel version of the `generate` method

        :param num_universities: number of universities to generate
        :param index: the index at whitch the generation process starts
        :param generator_seed:  a seed used by the generator
        :return: None
        """

        def job_finished(res):
            pass

        unis_per_worker = 10

        pool = Pool()
        for idx in xrange(index, num_universities + index, unis_per_worker):
            pool.apply_async(gen_uni, (unis_per_worker, idx, generator_seed), callback=job_finished)

        pool.close()
        pool.join()

        # convert all to ntriples
        self._log.info('converting to ntriples ... ')
        rdf_converter = RaptorRdf()
        rdf_converter('.', destination_format='ntriples', buffer_size=16, clear=True)

        # now concatenate all files belonging to 1 university together
        files = os.listdir('.')
        sfiles = ' '.join(files)
        uni_files = lambda uni: re.findall(r'University%d_[0-9]+\.nt' % uni, sfiles)
        for uni in xrange(num_universities):
            ufiles = uni_files(uni)

            with io.open('University%d.nt' % uni, 'w+') as UNI:
                for upart in ufiles:
                    with io.open(upart, 'r+') as UPART:
                        UNI.write(UPART.read())
                    sh.rm(upart)

        self._log.info('done')

<commit_msg>code reformatting<commit_after>import os
import io
import sh
import sys
import re
from multiprocessing import Pool, cpu_count
from rdftools.tools.base import RdfTool
from rdftools.tools.raptor import RaptorRdf

__author__ = 'basca'

_CLASSPATH = os.path.join(os.path.split(__file__)[0], 'classpath')

def gen_uni(num_unis, idx, seed):
    lubm = Lubm()
    lubm.generate(num_unis, idx, seed)

class Lubm(RdfTool):
    def __init__(self, ontology=None, *args, **kwargs):
        super(Lubm, self).__init__(*args, **kwargs)
        global _CLASSPATH
        self._classpath = _CLASSPATH
        self._ontology = ontology if ontology else 'http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl'

    @property
    def ontology(self):
        """
        the lubm ontology, used internally by the generator
        :return: the lubm ontology
        """
        return self._ontology

    @property
    def env_var(self):
        """
        the lubm environment variable
        :return: the lubm environment variable
        """
        return 'LUBM_PATH'

    @property
    def classpath(self):
        """
        the classpath of the bundled lubm, if the 'LUBM_PATH' env var is set, the path is taken from there
        :return: the classpath
        """
        cp = self._classpath
        if self.env_var in os.environ:
            cp = os.environ.get(self.env_var, None)
        return cp if cp else self._classpath

    def generate(self, num_universities, index, generator_seed):
        """
        the method calls the bundled lubm generator with the specified parameters

        :param num_universities: number of universities to generate
        :param index: the index at whitch the generation process starts
        :param generator_seed:  a seed used by the generator
        """
        self._log.info('[generate] Lubm num_unis={0}, index={1}'.format(num_universities, index))
        sys.stdout.flush()
        output = sh.java('-cp', self.classpath, 'edu.lehigh.swat.bench.uba.Generator', '-univ', num_universities,
                         '-index', index,
                         '-seed', generator_seed, '-onto', self.ontology)
        if output.exit_code:
            self._log.error('an error occured, while running lubm, output: \n{0}'.format(output))

    def _run(self, num_universities, index=0, generator_seed=0):
        """
        a paralel version of the `generate` method

        :param num_universities: number of universities to generate
        :param index: the index at whitch the generation process starts
        :param generator_seed:  a seed used by the generator
        :return: None
        """

        def job_finished(res):
            pass

        pool = Pool()
        unis_per_worker = 10 if num_universities > pool._processes else num_universities / pool._processes
        for idx in xrange(index, num_universities + index, unis_per_worker):
            pool.apply_async(gen_uni, (unis_per_worker, idx, generator_seed), callback=job_finished)

        pool.close()
        pool.join()

        # convert all to ntriples
        self._log.info('converting to ntriples ... ')
        rdf_converter = RaptorRdf()
        rdf_converter('.', destination_format='ntriples', buffer_size=16, clear=True)

        # now concatenate all files belonging to 1 university together
        files = os.listdir('.')
        sfiles = ' '.join(files)
        uni_files = lambda uni: re.findall(r'University%d_[0-9]+\.nt' % uni, sfiles)
        for uni in xrange(num_universities):
            ufiles = uni_files(uni)

            with io.open('University%d.nt' % uni, 'w+') as UNI:
                for upart in ufiles:
                    with io.open(upart, 'r+') as UPART:
                        UNI.write(UPART.read())
                    sh.rm(upart)

        self._log.info('done')

<|endoftext|>


<commit_before>class ToolConfig():
    '''
    Object used to represent LTI configuration.

    Capable of creating and reading the Common Cartridge XML representation of
    LTI links as described here:
        http://www.imsglobal.org/LTI/v1p1pd/ltiIMGv1p1pd.html#_Toc309649689

    TODO: Usage description
    '''
    def __init__(self, opts = {}):
        '''
        Create a new ToolConfig with the given options.
        '''
        self.custom_params = opts.pop('custom_params') or {}
        self.extensions = opts.pop('extensions') or {}

        for opt in opts:
            self.
<commit_msg>Lots of functionality for tool_config.py<commit_after>from collections import defaultdict

# Namespaces used for parsing configuration XML
LTI_NAMESPACES = {
        "xmlns": 'http://www.imsglobal.org/xsd/imslticc_v1p0',
        "blti": 'http://www.imsglobal.org/xsd/imsbasiclti_v1p0',
        "lticm": 'http://www.imsglobal.org/xsd/imslticm_v1p0',
        "lticp": 'http://www.imsglobal.org/xsd/imslticp_v1p0',
        }

class ToolConfig():
    '''
    Object used to represent LTI configuration.

    Capable of creating and reading the Common Cartridge XML representation of
    LTI links as described here:
        http://www.imsglobal.org/LTI/v1p1pd/ltiIMGv1p1pd.html#_Toc309649689

    TODO: Usage description
    '''
    def __init__(self, opts = defaultdict(lambda: None)):
        '''
        Create a new ToolConfig with the given options.
        '''
        self.custom_params = opts.pop('custom_params') or\
                defaultdict(lambda: None)
        self.extensions = opts.pop('extensions') or\
                defaultdict(lambda: None)
        self.opts = defaultdict(lambda: None)

        for (key, val) in opts.iteritems():
            self.opts[key] = val

    def create_from_xml(self):
        '''
        Create a ToolConfig from the given XML.
        '''
        # TODO: WTF!?
        pass

    def set_custom_param(self, key, val):
        '''
        Set a custom parameter to provided value.
        '''
        self.custom_params[key] = val

    def get_custom_param(self, key):
        '''
        Gets a custom parameter. It not yet set, returns None object.
        '''
        return self.custom_params[key]

    def set_ext_params(self, ext_key, ext_params):
        '''
        Set the extension parameters for a specific vendor.
        '''
        self.extensions[ext_key] = ext_params

    def get_ext_params(self, ext_key):
        '''
        Get extension paramaters for provided extension. It not set, returns None object.
        '''
        return self.extensions[ext_key]

    def set_ext_param(self, ext_key, param_key, val):
        '''
        Set the provided parameter in a set of extension parameters.
        '''
        if not self.extensions[ext_key]:
            self.extensions[ext_key] = defaultdict(lambda: None)
        self.extensions[ext_key][param_key] = val

    def get_ext_param(self, ext_key, param_key):
        '''
        Get specific param in set of provided extension parameters.
        '''
        return self.extensions[ext_key][param_key] if self.extensions[ext_key]\
                else None

    def process_xml(xml):
        '''
        Parse tool configuration data out of the Common Cartridge LTI link XML.
        '''
        # TODO Parse XML
        pass

    def to_xml(opts = defaultdict(lambda: None)):
        '''
        Generate XML from the current settings.
        '''
        # TODO Generte XML
        pass

<|endoftext|>


<commit_before>from django.conf.urls import patterns, url
from django.contrib import admin
from django.db.models import Q
from django.http import HttpResponse, HttpResponseBadRequest
from django.core import urlresolvers
from django.core.urlresolvers import reverse
from django.conf import settings
import os
import json, datetime
from rdrf.utils import de_camelcase
from rdrf.models import Registry, RegistryForm
from registry.utils import get_static_url, get_working_groups, get_registries
from admin_forms import *
from models import *
from rdrf.dynamic_data import DynamicDataWrapper
from django.contrib.auth import get_user_model
import logging
logger = logging.getLogger("registry_log")

class DoctorAdmin(admin.ModelAdmin):
    search_fields = ["family_name", "given_names"]

class PatientDoctorAdmin(admin.TabularInline):
    fields = ["relationship", "doctor"]
    form = PatientDoctorForm
    model = PatientDoctor
    extra = 0

class PatientConsentAdmin(admin.TabularInline):
    model = PatientConsent
    extra = 1

class PatientAddressAdmin(admin.StackedInline):
    model = PatientAddress
    form = PatientAddressForm
    extra = 0

class RegistryFilter(admin.SimpleListFilter):
    title = "Registry"
    parameter_name = 'registry'

    def lookups(self, request, model_admin):

        if request.user.is_superuser:
            reg_list = Registry.objects.all()
        else:
            reg_list = get_user_model().objects.get(username=request.user).registry.all()

        regs = []
        for reg in reg_list:
            regs.append((reg.id, reg.name))

        return regs

    def queryset(self, request, queryset):
        if request.GET.__contains__('registry'):
            reg = request.GET.__getitem__('registry')
            return queryset.filter(rdrf_registry__id__exact=reg)

        return queryset

class PatientAdmin(admin.ModelAdmin):
    def __init__(self, *args, **kwargs):
        super(PatientAdmin, self).__init__(*args, **kwargs)
        self.list_display_links = (None, )

    app_url = os.environ.get("SCRIPT_NAME", "")
    form = PatientForm
    request = None

    inlines = [PatientAddressAdmin, PatientConsentAdmin, PatientDoctorAdmin]
    search_fields = ["family_name", "given_names"]
    list_display = ['full_name', 'working_groups_display', 'get_reg_list', 'date_of_birth', 'demographic_btn', 'data_modules_btn']
    list_filter = [RegistryFilter,]

    def full_name(self, obj):
       return obj.__unicode__()

    full_name.short_description = 'Name'

    def demographic_btn(self, obj):
        return "Details" % reverse('admin:patients_patient_change', args=(obj.id,))

    demographic_btn.allow_tags = True
    demographic_btn.short_description = 'Demographics'

    def data_modules_btn(self, obj):
        if obj.rdrf_registry.count() == 0:
            return "No registry assigned"

        rdrf_id = self.request.GET.get('registry')

        if not rdrf_id and Registry.objects.count() > 1:
            return "Please filter registry"

        def nice_name(name):
            try:
                return de_camelcase(name)
            except:
                return name

        rdrf = Registry.objects.get(pk=rdrf_id)
        not_generated = lambda frm: not frm.name.startswith(rdrf.generated_questionnaire_name)
        forms = [ f for f in RegistryForm.objects.filter(registry=rdrf).order_by('position') if not_generated(f) ]

        content = ''

        if not forms:
            content = "No modules available"

        if len(forms) == 1:
            url = reverse('registry_form', args=(rdrf.code, forms[0].id, obj.id))
            return "Details" % url

        for form in forms:
            url = reverse('registry_form', args=(rdrf.code, form.id, obj.id))
            content += "%s
" % (url, nice_name(form.name))

        return "<button type='button' class='btn btn-info btn-small' data-toggle='popover' data-content='%s' id='data-modules-btn'>Show Modules</button>" % content

    data_modules_btn.allow_tags = True
    data_modules_btn.short_description = 'Data Modules'

    def get_form(self, request, obj=None, **kwargs):
        # NB. This method returns a form class
        user =  get_user_model().objects.get(username=request.user)
        registry_specific_fields = self._get_registry_specific_patient_fields(user)
        self.form = self._add_registry_specific_fields(self.form, registry_specific_fields)
        form = super(PatientAdmin, self).get_form(request, obj, **kwargs)
        form.user = user
        form.is_superuser = request.user.is_superuser
        return form

    def _add_registry_specific_fields(self, form_class, registry_specific_fields_dict):
        additional_fields = {}
        for reg_code in registry_specific_fields_dict:
            field_pairs = registry_specific_fields_dict[reg_code]
            for cde, field_object in field_pairs:
                additional_fields[cde.code] = field_object

        new_form_class = type(form_class.__name__, (form_class,), additional_fields)
        return new_form_class

    def _get_registry_specific_patient_fields(self, user):
        """
        :param user:
        :return: a dictionary mapping registry codes to lists of pairs of cde models and field objects
        """
        result_dict = {}
        for registry in user.registry.all():
            patient_cde_field_pairs = registry.patient_fields
            if patient_cde_field_pairs:
                result_dict[registry.code] = patient_cde_field_pairs

        return result_dict

    def _get_registry_specific_fieldsets(self, user):
        reg_spec_field_defs = self._get_registry_specific_patient_fields(user)
        fieldsets = []
        for reg_code in reg_spec_field_defs:
            cde_field_pairs = reg_spec_field_defs[reg_code]
            fieldset_title = "%s Specific Fields" % reg_code
            field_dict = {"fields" : [ pair[0].code for pair in cde_field_pairs ]} # pair up cde name and field object generated from that cde
            fieldsets.append((fieldset_title, field_dict))
        return fieldsets

    def create_fieldset(self, user):

        consent = ("Consent", {
            "fields":(
                "consent",
                "consent_clinical_trials",
                "consent_sent_information",
             )
        })

        rdrf_registry = ("Registry", {
            "fields":(
                "rdrf_registry",
            )
        })

        personal_details = ("Personal Details", {})

        personal_details_fields = [
                                   "working_group",
                                   "working_groups",
                                   "family_name",
                                   "given_names",
                                   "maiden_name",
                                   "umrn",
                                   "date_of_birth",
                                   "place_of_birth",
                                   "country_of_birth",
                                   "ethnic_origin",
                                   "sex",
                                   "home_phone",
                                   "mobile_phone",
                                   "work_phone",
                                   "email"
                                   ]

        # fix for Trac #3, the field is now always displayed, but readonly for not superuser users, see get_readonly_fields below
        personal_details_fields.append("active")
        personal_details_fields.append("inactive_reason")

        personal_details[1]["fields"] = tuple(personal_details_fields)

        next_of_kin = ("Next of Kin", {
            "fields":
            ("next_of_kin_family_name",
             "next_of_kin_given_names",
             "next_of_kin_relationship",
             "next_of_kin_address",
             "next_of_kin_suburb",
             "next_of_kin_state",
             "next_of_kin_postcode",
             "next_of_kin_home_phone",
             "next_of_kin_mobile_phone",
             "next_of_kin_work_phone",
             "next_of_kin_email",
             "next_of_kin_parent_place_of_birth"
             )})

        fieldset = [consent, rdrf_registry, personal_details, next_of_kin]
        fieldset.extend(self._get_registry_specific_fieldsets(user))
        return fieldset

    def save_form(self, request, form, change):
        """
        We override save_form to support saving (some) registry specific data to Mongo
        The wrinkle is that the instance has not yet been saved, so if the user is adding a patient
        there will be no primary key ( all mongo data is linked to django via a primary key). We work around tbis, by tagging the data to be saved in Mongo
        on the instance and overriding save_model ( see below )
        """
        mongo_patient_data = {}
        instance = form.save(commit=False)
        registry_specific_fields_dict = self._get_registry_specific_patient_fields(request.user)

        for reg_code in registry_specific_fields_dict:
            mongo_patient_data[reg_code] = {}
            registry_specific_fields = registry_specific_fields_dict[reg_code]
            for cde, field_object in registry_specific_fields:
                field_name = cde.name
                cde_code = cde.code
                field_value = request.POST[cde.code]
                mongo_patient_data[reg_code][cde_code] = field_value

                logger.debug("specific field for %s %s = %s" % (reg_code, field_name, field_value))

        if mongo_patient_data:
            instance.mongo_patient_data = mongo_patient_data

        return instance

    def save_model(self, request, obj, form, change):
        """
        Override ModelAdmin code to allow us to save the registry specific patient to Mongo ...
        """
        obj.save()

        if hasattr(obj, 'mongo_patient_data'):
            patient_id = obj.pk
            self._save_registry_specific_data_in_mongo(obj)

    def get_fieldsets(self, request, obj=None):
        return self.create_fieldset(request.user)

    def _save_registry_specific_data_in_mongo(self, patient):
        data = patient.mongo_patient_data
        mongo_wrapper = DynamicDataWrapper(patient)
        mongo_wrapper.save_registry_specific_data(data)

    def get_readonly_fields(self, request, obj=None):
        if request.user.is_superuser:
            return []
        else:
            #return ['active'] # NB this seems to run into a mango bug that prevents Add Patient being used by non-superuser
            return []

    def formfield_for_dbfield(self, dbfield, *args, **kwargs):
        from registry.groups.models import WorkingGroup

        request = kwargs.get('request')
        user = request.user
        # Restrict normal users to their own working group.
        if dbfield.name == "working_group" and not user.is_superuser:
            user = get_user_model().objects.get(username=user) # get the user's associated objects
            kwargs["queryset"] = WorkingGroup.objects.filter(id__in = get_working_groups(user))
            #kwargs["queryset"] = WorkingGroup.objects

        if dbfield.name == "rdrf_registry" and not user.is_superuser:
            user = get_user_model().objects.get(username=user)
            kwargs["queryset"] = Registry.objects.filter(id__in=[reg.id for reg in user.registry.all()])

        return super(PatientAdmin, self).formfield_for_dbfield(dbfield, *args, **kwargs)

    def get_urls(self):
        urls = super(PatientAdmin, self).get_urls()
        local_urls = patterns("",
            url(r"search/(.*)$", self.admin_site.admin_view(self.search), name="patient_search")
        )
        return local_urls + urls

    def queryset(self, request):
        self.request = request
        import registry.groups.models

        if request.user.is_superuser:
            return Patient.objects.all()

        user = get_user_model().objects.get(username=request.user)
        return Patient.objects.get_filtered(user)

    def search(self, request, term):
        # We have to do this against the result of self.queryset() to avoid
        # leaking patient details across working groups.
        queryset = self.queryset(request)

        try:
            # Check if the search term is numeric, in which case it's a record
            # ID.
            patient = queryset.get(id=int(term))
            response = [[patient.id, unicode(patient), unicode(patient.date_of_birth)]]
        except ValueError:
            # Guess not.
            patients = queryset.filter(Q(family_name__icontains=term) | Q(given_names__icontains=term)).order_by("family_name", "given_names")
            response = [[patient.id, unicode(patient), unicode(patient.date_of_birth)] for patient in patients]
        except Patient.DoesNotExist:
            response = []

        return HttpResponse(json.dumps(response), mimetype="application/json")

    def diagnosis_last_update(self, obj):
        return "%s" % obj.patient_diagnosis.updated

    diagnosis_last_update.allow_tags = True
    diagnosis_last_update.short_description = "Last Updated"

    def progress_graph(self, obj):
        if not hasattr(obj, 'patient_diagnosis'):
            return ''
        graph_html = '' % urlresolvers.reverse('admin:{0}_diagnosis_change'.format(obj.patient_diagnosis._meta.app_label), args=(obj.id,))
        graph_html += obj.patient_diagnosis.progress_graph()
        graph_html += ''
        return graph_html

    progress_graph.allow_tags = True
    progress_graph.short_description = "Diagnosis Entry Progress"

    def moleculardata_entered(self, obj):
        if not hasattr(obj, 'moleculardatasma') or not hasattr(obj.moleculardatasma, 'variationsma_set') or not obj.moleculardatasma.variationsma_set.all():
            return ''

        imagefile = 'tick.png'

        genetic_url = '' % urlresolvers.reverse('admin:genetic_moleculardatasma_change', args=(obj.id,))
        genetic_url += '' % get_static_url("images/" + imagefile)
        genetic_url += ''
        return genetic_url

    moleculardata_entered.allow_tags = True
    moleculardata_entered.short_description = "Genetic Data"

    def freshness(self, obj):
        """Used to show how recently the diagnosis was updated"""
        if not hasattr(obj, 'patient_diagnosis'):
            return ''

        delta = datetime.datetime.now() - obj.patient_diagnosis.updated
        age = delta.days

        if age > 365:
            imagefile = 'cross.png'
        else:
            imagefile = 'tick.png'

        return '' % get_static_url("images/" + imagefile)

    freshness.allow_tags = True
    freshness.short_description = "Currency (updated in the last 365 days)"

    def last_updated(self, obj):
        if not hasattr(obj, 'diagnosis'):
            return ''
        delta = datetime.datetime.now() - obj.diagnosis.updated
        age = delta.days

        if age == 0:
            return 'today'
        if age == 1:
            return 'yesterday'
        else:
            return '%s days ago' % age

    last_updated.allow_tags = True
    last_updated.short_description = "Last updated"

class StateAdmin(admin.ModelAdmin):
    list_display = ["name"]
    search_fields = ["name"]

class NextOfKinRelationshipAdmin(admin.ModelAdmin):
    model = NextOfKinRelationship

class AddressTypeAdmin(admin.ModelAdmin):
    model = AddressType
    list_display = ('type', 'description')

admin.site.register(Doctor, DoctorAdmin)
admin.site.register(Patient, PatientAdmin)
admin.site.register(State, StateAdmin)
admin.site.register(NextOfKinRelationship, NextOfKinRelationshipAdmin)
admin.site.register(AddressType, AddressTypeAdmin)

admin.site.disable_action('delete_selected')
<commit_msg>RDR-644 removed obsolete working_group field from patient admin form<commit_after>from django.conf.urls import patterns, url
from django.contrib import admin
from django.db.models import Q
from django.http import HttpResponse, HttpResponseBadRequest
from django.core import urlresolvers
from django.core.urlresolvers import reverse
from django.conf import settings
import os
import json, datetime
from rdrf.utils import de_camelcase
from rdrf.models import Registry, RegistryForm
from registry.utils import get_static_url, get_working_groups, get_registries
from admin_forms import *
from models import *
from rdrf.dynamic_data import DynamicDataWrapper
from django.contrib.auth import get_user_model
import logging
logger = logging.getLogger("registry_log")

class DoctorAdmin(admin.ModelAdmin):
    search_fields = ["family_name", "given_names"]

class PatientDoctorAdmin(admin.TabularInline):
    fields = ["relationship", "doctor"]
    form = PatientDoctorForm
    model = PatientDoctor
    extra = 0

class PatientConsentAdmin(admin.TabularInline):
    model = PatientConsent
    extra = 1

class PatientAddressAdmin(admin.StackedInline):
    model = PatientAddress
    form = PatientAddressForm
    extra = 0

class RegistryFilter(admin.SimpleListFilter):
    title = "Registry"
    parameter_name = 'registry'

    def lookups(self, request, model_admin):

        if request.user.is_superuser:
            reg_list = Registry.objects.all()
        else:
            reg_list = get_user_model().objects.get(username=request.user).registry.all()

        regs = []
        for reg in reg_list:
            regs.append((reg.id, reg.name))

        return regs

    def queryset(self, request, queryset):
        if request.GET.__contains__('registry'):
            reg = request.GET.__getitem__('registry')
            return queryset.filter(rdrf_registry__id__exact=reg)

        return queryset

class PatientAdmin(admin.ModelAdmin):
    def __init__(self, *args, **kwargs):
        super(PatientAdmin, self).__init__(*args, **kwargs)
        self.list_display_links = (None, )

    app_url = os.environ.get("SCRIPT_NAME", "")
    form = PatientForm
    request = None

    inlines = [PatientAddressAdmin, PatientConsentAdmin, PatientDoctorAdmin]
    search_fields = ["family_name", "given_names"]
    list_display = ['full_name', 'working_groups_display', 'get_reg_list', 'date_of_birth', 'demographic_btn', 'data_modules_btn']
    list_filter = [RegistryFilter,]

    def full_name(self, obj):
       return obj.__unicode__()

    full_name.short_description = 'Name'

    def demographic_btn(self, obj):
        return "Details" % reverse('admin:patients_patient_change', args=(obj.id,))

    demographic_btn.allow_tags = True
    demographic_btn.short_description = 'Demographics'

    def data_modules_btn(self, obj):
        if obj.rdrf_registry.count() == 0:
            return "No registry assigned"

        rdrf_id = self.request.GET.get('registry')

        if not rdrf_id and Registry.objects.count() > 1:
            return "Please filter registry"

        def nice_name(name):
            try:
                return de_camelcase(name)
            except:
                return name

        rdrf = Registry.objects.get(pk=rdrf_id)
        not_generated = lambda frm: not frm.name.startswith(rdrf.generated_questionnaire_name)
        forms = [ f for f in RegistryForm.objects.filter(registry=rdrf).order_by('position') if not_generated(f) ]

        content = ''

        if not forms:
            content = "No modules available"

        if len(forms) == 1:
            url = reverse('registry_form', args=(rdrf.code, forms[0].id, obj.id))
            return "Details" % url

        for form in forms:
            url = reverse('registry_form', args=(rdrf.code, form.id, obj.id))
            content += "%s
" % (url, nice_name(form.name))

        return "<button type='button' class='btn btn-info btn-small' data-toggle='popover' data-content='%s' id='data-modules-btn'>Show Modules</button>" % content

    data_modules_btn.allow_tags = True
    data_modules_btn.short_description = 'Data Modules'

    def get_form(self, request, obj=None, **kwargs):
        # NB. This method returns a form class
        user =  get_user_model().objects.get(username=request.user)
        registry_specific_fields = self._get_registry_specific_patient_fields(user)
        self.form = self._add_registry_specific_fields(self.form, registry_specific_fields)
        form = super(PatientAdmin, self).get_form(request, obj, **kwargs)
        form.user = user
        form.is_superuser = request.user.is_superuser
        return form

    def _add_registry_specific_fields(self, form_class, registry_specific_fields_dict):
        additional_fields = {}
        for reg_code in registry_specific_fields_dict:
            field_pairs = registry_specific_fields_dict[reg_code]
            for cde, field_object in field_pairs:
                additional_fields[cde.code] = field_object

        new_form_class = type(form_class.__name__, (form_class,), additional_fields)
        return new_form_class

    def _get_registry_specific_patient_fields(self, user):
        """
        :param user:
        :return: a dictionary mapping registry codes to lists of pairs of cde models and field objects
        """
        result_dict = {}
        for registry in user.registry.all():
            patient_cde_field_pairs = registry.patient_fields
            if patient_cde_field_pairs:
                result_dict[registry.code] = patient_cde_field_pairs

        return result_dict

    def _get_registry_specific_fieldsets(self, user):
        reg_spec_field_defs = self._get_registry_specific_patient_fields(user)
        fieldsets = []
        for reg_code in reg_spec_field_defs:
            cde_field_pairs = reg_spec_field_defs[reg_code]
            fieldset_title = "%s Specific Fields" % reg_code
            field_dict = {"fields" : [ pair[0].code for pair in cde_field_pairs ]} # pair up cde name and field object generated from that cde
            fieldsets.append((fieldset_title, field_dict))
        return fieldsets

    def create_fieldset(self, user):

        consent = ("Consent", {
            "fields":(
                "consent",
                "consent_clinical_trials",
                "consent_sent_information",
             )
        })

        rdrf_registry = ("Registry", {
            "fields":(
                "rdrf_registry",
            )
        })

        personal_details = ("Personal Details", {})

        personal_details_fields = [
                                   "working_groups",
                                   "family_name",
                                   "given_names",
                                   "maiden_name",
                                   "umrn",
                                   "date_of_birth",
                                   "place_of_birth",
                                   "country_of_birth",
                                   "ethnic_origin",
                                   "sex",
                                   "home_phone",
                                   "mobile_phone",
                                   "work_phone",
                                   "email"
                                   ]

        # fix for Trac #3, the field is now always displayed, but readonly for not superuser users, see get_readonly_fields below
        personal_details_fields.append("active")
        personal_details_fields.append("inactive_reason")

        personal_details[1]["fields"] = tuple(personal_details_fields)

        next_of_kin = ("Next of Kin", {
            "fields":
            ("next_of_kin_family_name",
             "next_of_kin_given_names",
             "next_of_kin_relationship",
             "next_of_kin_address",
             "next_of_kin_suburb",
             "next_of_kin_state",
             "next_of_kin_postcode",
             "next_of_kin_home_phone",
             "next_of_kin_mobile_phone",
             "next_of_kin_work_phone",
             "next_of_kin_email",
             "next_of_kin_parent_place_of_birth"
             )})

        fieldset = [consent, rdrf_registry, personal_details, next_of_kin]
        fieldset.extend(self._get_registry_specific_fieldsets(user))
        return fieldset

    def save_form(self, request, form, change):
        """
        We override save_form to support saving (some) registry specific data to Mongo
        The wrinkle is that the instance has not yet been saved, so if the user is adding a patient
        there will be no primary key ( all mongo data is linked to django via a primary key). We work around tbis, by tagging the data to be saved in Mongo
        on the instance and overriding save_model ( see below )
        """
        mongo_patient_data = {}
        instance = form.save(commit=False)
        registry_specific_fields_dict = self._get_registry_specific_patient_fields(request.user)

        for reg_code in registry_specific_fields_dict:
            mongo_patient_data[reg_code] = {}
            registry_specific_fields = registry_specific_fields_dict[reg_code]
            for cde, field_object in registry_specific_fields:
                field_name = cde.name
                cde_code = cde.code
                field_value = request.POST[cde.code]
                mongo_patient_data[reg_code][cde_code] = field_value

                logger.debug("specific field for %s %s = %s" % (reg_code, field_name, field_value))

        if mongo_patient_data:
            instance.mongo_patient_data = mongo_patient_data

        return instance

    def save_model(self, request, obj, form, change):
        """
        Override ModelAdmin code to allow us to save the registry specific patient to Mongo ...
        """
        obj.save()

        if hasattr(obj, 'mongo_patient_data'):
            patient_id = obj.pk
            self._save_registry_specific_data_in_mongo(obj)

    def get_fieldsets(self, request, obj=None):
        return self.create_fieldset(request.user)

    def _save_registry_specific_data_in_mongo(self, patient):
        data = patient.mongo_patient_data
        mongo_wrapper = DynamicDataWrapper(patient)
        mongo_wrapper.save_registry_specific_data(data)

    def get_readonly_fields(self, request, obj=None):
        if request.user.is_superuser:
            return []
        else:
            #return ['active'] # NB this seems to run into a mango bug that prevents Add Patient being used by non-superuser
            return []

    def formfield_for_dbfield(self, dbfield, *args, **kwargs):
        from registry.groups.models import WorkingGroup

        request = kwargs.get('request')
        user = request.user
        # Restrict normal users to their own working group.
        if dbfield.name == "working_groups" and not user.is_superuser:
            user = get_user_model().objects.get(username=user) # get the user's associated objects
            kwargs["queryset"] = WorkingGroup.objects.filter(id__in = get_working_groups(user))
            #kwargs["queryset"] = WorkingGroup.objects

        if dbfield.name == "rdrf_registry" and not user.is_superuser:
            user = get_user_model().objects.get(username=user)
            kwargs["queryset"] = Registry.objects.filter(id__in=[reg.id for reg in user.registry.all()])

        return super(PatientAdmin, self).formfield_for_dbfield(dbfield, *args, **kwargs)

    def get_urls(self):
        urls = super(PatientAdmin, self).get_urls()
        local_urls = patterns("",
            url(r"search/(.*)$", self.admin_site.admin_view(self.search), name="patient_search")
        )
        return local_urls + urls

    def queryset(self, request):
        self.request = request
        import registry.groups.models

        if request.user.is_superuser:
            return Patient.objects.all()

        user = get_user_model().objects.get(username=request.user)
        return Patient.objects.get_filtered(user)

    def search(self, request, term):
        # We have to do this against the result of self.queryset() to avoid
        # leaking patient details across working groups.
        queryset = self.queryset(request)

        try:
            # Check if the search term is numeric, in which case it's a record
            # ID.
            patient = queryset.get(id=int(term))
            response = [[patient.id, unicode(patient), unicode(patient.date_of_birth)]]
        except ValueError:
            # Guess not.
            patients = queryset.filter(Q(family_name__icontains=term) | Q(given_names__icontains=term)).order_by("family_name", "given_names")
            response = [[patient.id, unicode(patient), unicode(patient.date_of_birth)] for patient in patients]
        except Patient.DoesNotExist:
            response = []

        return HttpResponse(json.dumps(response), mimetype="application/json")

    def diagnosis_last_update(self, obj):
        return "%s" % obj.patient_diagnosis.updated

    diagnosis_last_update.allow_tags = True
    diagnosis_last_update.short_description = "Last Updated"

    def progress_graph(self, obj):
        if not hasattr(obj, 'patient_diagnosis'):
            return ''
        graph_html = '' % urlresolvers.reverse('admin:{0}_diagnosis_change'.format(obj.patient_diagnosis._meta.app_label), args=(obj.id,))
        graph_html += obj.patient_diagnosis.progress_graph()
        graph_html += ''
        return graph_html

    progress_graph.allow_tags = True
    progress_graph.short_description = "Diagnosis Entry Progress"

    def moleculardata_entered(self, obj):
        if not hasattr(obj, 'moleculardatasma') or not hasattr(obj.moleculardatasma, 'variationsma_set') or not obj.moleculardatasma.variationsma_set.all():
            return ''

        imagefile = 'tick.png'

        genetic_url = '' % urlresolvers.reverse('admin:genetic_moleculardatasma_change', args=(obj.id,))
        genetic_url += '' % get_static_url("images/" + imagefile)
        genetic_url += ''
        return genetic_url

    moleculardata_entered.allow_tags = True
    moleculardata_entered.short_description = "Genetic Data"

    def freshness(self, obj):
        """Used to show how recently the diagnosis was updated"""
        if not hasattr(obj, 'patient_diagnosis'):
            return ''

        delta = datetime.datetime.now() - obj.patient_diagnosis.updated
        age = delta.days

        if age > 365:
            imagefile = 'cross.png'
        else:
            imagefile = 'tick.png'

        return '' % get_static_url("images/" + imagefile)

    freshness.allow_tags = True
    freshness.short_description = "Currency (updated in the last 365 days)"

    def last_updated(self, obj):
        if not hasattr(obj, 'diagnosis'):
            return ''
        delta = datetime.datetime.now() - obj.diagnosis.updated
        age = delta.days

        if age == 0:
            return 'today'
        if age == 1:
            return 'yesterday'
        else:
            return '%s days ago' % age

    last_updated.allow_tags = True
    last_updated.short_description = "Last updated"

class StateAdmin(admin.ModelAdmin):
    list_display = ["name"]
    search_fields = ["name"]

class NextOfKinRelationshipAdmin(admin.ModelAdmin):
    model = NextOfKinRelationship

class AddressTypeAdmin(admin.ModelAdmin):
    model = AddressType
    list_display = ('type', 'description')

admin.site.register(Doctor, DoctorAdmin)
admin.site.register(Patient, PatientAdmin)
admin.site.register(State, StateAdmin)
admin.site.register(NextOfKinRelationship, NextOfKinRelationshipAdmin)
admin.site.register(AddressType, AddressTypeAdmin)

admin.site.disable_action('delete_selected')
<|endoftext|>