From fb17500eeb13044eeacb2e8c10b73d3d8824f728 Mon Sep 17 00:00:00 2001 From: Edwin Henneken Date: Tue, 8 Dec 2020 13:21:03 -0500 Subject: [PATCH 1/4] calculate updated record diff in backend --- adsws/feedback/utils.py | 30 +++++++ adsws/feedback/views.py | 95 ++++++++++++---------- adsws/tests/stubdata/corrected_abstract.py | 2 +- requirements.txt | 1 + 4 files changed, 82 insertions(+), 46 deletions(-) diff --git a/adsws/feedback/utils.py b/adsws/feedback/utils.py index 8415f2d..894e02d 100644 --- a/adsws/feedback/utils.py +++ b/adsws/feedback/utils.py @@ -6,6 +6,8 @@ from flask import current_app from flask.ext.mail import Message import json +#from dictdiffer import diff +from jsondiff import diff def send_feedback_email(name, sender, subject, data, attachments=None): # Allow the default recipient to be overriden depending on email subject @@ -23,6 +25,32 @@ def send_feedback_email(name, sender, subject, data, attachments=None): current_app.logger.info('Successfully sent email: data submitted by {0}, sent to {1} (form: {2})'.format(sender, email, subject)) return msg +def make_diff(original, updated): + + diffdata = diff(original, updated) + + results = '' + if diffdata.has_key('comments'): + results += "\n\nComments: %s\n\n" % diffdata['comments'] + for field, changes in diffdata.items(): + if field == 'comments': + continue + results += ">>>> %s\n" % field + if isinstance(changes,dict): + for k,v in changes.items(): + results += "{0} -- {1}\n".format(k,v) + elif isinstance(changes,list): + for item in changes: + try: + results += "{0}\t{1}\n".format(updated['bibcode'], item.replace('(bibcode) ','').replace('(reference) ','')) + except: + results += str(item) + "\n" + else: + results += str(changes) + "\n" + results += ">>>>\n" + + return results + def err(error_dictionary): """ Formats the error response as wanted by the Flask app @@ -31,3 +59,5 @@ def err(error_dictionary): :return: tuple of error message and error number """ return {'error': error_dictionary['body']}, error_dictionary['number'] + + diff --git a/adsws/feedback/views.py b/adsws/feedback/views.py index 7433495..5a90dd8 100644 --- a/adsws/feedback/views.py +++ b/adsws/feedback/views.py @@ -12,7 +12,7 @@ from adsws.feedback.utils import err from adsws.accounts.utils import verify_recaptcha, get_post_data from werkzeug.exceptions import BadRequestKeyError -from utils import send_feedback_email +from utils import send_feedback_email, make_diff from urllib import unquote API_DOCS = 'https://github.com/adsabs/adsabs-dev-api' @@ -76,11 +76,15 @@ def create_email_body(post_data): email_data.pop(key, None) # Retrieve the appropriate template template = current_app.config['FEEDBACK_TEMPLATES'].get(email_data.get('_subject')) - # For abstract corrections, the POST payload has a "diff" attribute that contains + # For abstract corrections, we determine a diff from the original and updated records. + # In case this fails we fall back on the POST data "diff" attribute that contains # the updated fields in Github "diff" format, URL encoded. For display purposes, # this needs to be decoded. - if post_data.has_key('diff'): - email_data['diff'] = unquote(post_data['diff']) + if post_data.get('_subject') == 'Updated Record': + try: + email_data['diff'] = make_diff(post_data['original'], post_data['new']) + except: + email_data['diff'] = unquote(post_data.get('diff','')) # In the case of a new record the mail body will show a summary # In this summary it's easier to show a author list in the form of a string # We also attach the JSON data of the new record as a file @@ -107,12 +111,12 @@ def post(self): current_app.logger.info('Received feedback of type {0}: {1}'.format(post_data.get('_subject'), post_data)) - if not post_data.get('g-recaptcha-response', False) or \ - not verify_recaptcha(request): - current_app.logger.info('The captcha was not verified!') - return err(ERROR_UNVERIFIED_CAPTCHA) - else: - current_app.logger.info('Skipped captcha!') +# if not post_data.get('g-recaptcha-response', False) or \ +# not verify_recaptcha(request): +# current_app.logger.info('The captcha was not verified!') +# return err(ERROR_UNVERIFIED_CAPTCHA) +# else: +# current_app.logger.info('Skipped captcha!') # We only allow POST data from certain origins allowed_origins = [v for k,v in current_app.config.items() if k.endswith('_ORIGIN')] origin = post_data.get('origin', 'NA') @@ -169,41 +173,42 @@ def post(self): 'channel': channel, 'icon_emoji': icon_emoji } + print email_body # If we have an email body (should always be the case), send out the email - if email_body: - email_sent = False - try: - res = send_feedback_email(name, reply_to, post_data['_subject'], email_body, attachments=attachments) - email_sent = True - except Exception as e: - current_app.logger.error('Fatal error while processing feedback form data: {0}'.format(e)) - email_sent = False - if not email_sent: - # If the email could not be sent, we can still log the data submitted - current_app.logger.error('Sending of email failed. Feedback data submitted by {0}: {1}'.format(post_data.get('email'), post_data)) - return err(ERROR_EMAIL_NOT_SENT) - # If we have Slack data, post the message to Slack - if slack_data: - slack_data['text'] += '\n*sent to adshelp*: {0}'.format(email_sent) - try: - slack_response = requests.post( - url=current_app.config['FEEDBACK_SLACK_END_POINT'], - data=json.dumps(slack_data), - timeout=60 - ) - except (requests.exceptions.ConnectionError, requests.exceptions.Timeout): - return b'504 Gateway Timeout', 504 - current_app.logger.info('slack response: {0}' - .format(slack_response.status_code)) - - # Slack annoyingly redirects if you have the wrong end point - current_app.logger.info('Slack API' in slack_response.text) - - if 'Slack API' in slack_response.text: - return err(ERROR_WRONG_ENDPOINT) - elif slack_response.status_code == 200: - return {}, 200 - else: - return {'msg': 'Unknown error'}, slack_response.status_code +# if email_body: +# email_sent = False +# try: +# res = send_feedback_email(name, reply_to, post_data['_subject'], email_body, attachments=attachments) +# email_sent = True +# except Exception as e: +# current_app.logger.error('Fatal error while processing feedback form data: {0}'.format(e)) +# email_sent = False +# if not email_sent: +# # If the email could not be sent, we can still log the data submitted +# current_app.logger.error('Sending of email failed. Feedback data submitted by {0}: {1}'.format(post_data.get('email'), post_data)) +# return err(ERROR_EMAIL_NOT_SENT) +# # If we have Slack data, post the message to Slack +# if slack_data: +# slack_data['text'] += '\n*sent to adshelp*: {0}'.format(email_sent) +# try: +# slack_response = requests.post( +# url=current_app.config['FEEDBACK_SLACK_END_POINT'], +# data=json.dumps(slack_data), +# timeout=60 +# ) +# except (requests.exceptions.ConnectionError, requests.exceptions.Timeout): +# return b'504 Gateway Timeout', 504 +# current_app.logger.info('slack response: {0}' +# .format(slack_response.status_code)) +# +# # Slack annoyingly redirects if you have the wrong end point +# current_app.logger.info('Slack API' in slack_response.text) +# +# if 'Slack API' in slack_response.text: +# return err(ERROR_WRONG_ENDPOINT) +# elif slack_response.status_code == 200: +# return {}, 200 +# else: +# return {'msg': 'Unknown error'}, slack_response.status_code return {}, 200 diff --git a/adsws/tests/stubdata/corrected_abstract.py b/adsws/tests/stubdata/corrected_abstract.py index 1f1afa6..5ddf6d3 100644 --- a/adsws/tests/stubdata/corrected_abstract.py +++ b/adsws/tests/stubdata/corrected_abstract.py @@ -58,4 +58,4 @@ "diff": "%0A%20%20%3E%3E%3E%3E%20Title%0A%20%20test%0A%20%20%3C%3C%3C%3C%0A%0A%20%20%3E%3E%3E%3E%20Publication%0A%20%20test%0A%20%20%3C%3C%3C%3C%0A%0A%20%20%3E%3E%3E%3E%20PublicationDate%0A%20%202020-01%0A%20%20%3C%3C%3C%3C%0A%0A%20%20%3E%3E%3E%3E%20Comments%0A%20%20test%0A%20%20%3C%3C%3C%3C" } -response = 'From: Tim Hostetler\nAddress: twhostetler0@gmail.com\n\nCorrection for 2021NewA...8301464S:\n\n\n >>>> Title\n test\n <<<<\n\n >>>> Publication\n test\n <<<<\n\n >>>> PublicationDate\n 2020-01\n <<<<\n\n >>>> Comments\n test\n <<<<' \ No newline at end of file +response = 'From: Tim Hostetler\nAddress: twhostetler0@gmail.com\n\nCorrection for 2021NewA...8301464S:\n\n>>>> $delete\n2021NewA...8301464S\temail\n2021NewA...8301464S\trecaptcha\n2021NewA...8301464S\tentryType\n2021NewA...8301464S\tname\n>>>>\n>>>> collection\n2021NewA...8301464S\tastronomy\n2021NewA...8301464S\tphysics\n>>>>\n' \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b2e635e..185f85d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,3 +23,4 @@ alembic==0.8.1 redis==2.10.5 flask-consulate==0.1.2 email-validator==1.1.0 +jsondiff==1.2.0 From 32428ce0105fcc6c699d55e156c6417800c607fe Mon Sep 17 00:00:00 2001 From: Edwin Henneken Date: Tue, 8 Dec 2020 13:39:00 -0500 Subject: [PATCH 2/4] forgot to undo some commenting --- adsws/feedback/views.py | 84 ++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/adsws/feedback/views.py b/adsws/feedback/views.py index 5a90dd8..c24cb50 100644 --- a/adsws/feedback/views.py +++ b/adsws/feedback/views.py @@ -111,12 +111,12 @@ def post(self): current_app.logger.info('Received feedback of type {0}: {1}'.format(post_data.get('_subject'), post_data)) -# if not post_data.get('g-recaptcha-response', False) or \ -# not verify_recaptcha(request): -# current_app.logger.info('The captcha was not verified!') -# return err(ERROR_UNVERIFIED_CAPTCHA) -# else: -# current_app.logger.info('Skipped captcha!') + if not post_data.get('g-recaptcha-response', False) or \ + not verify_recaptcha(request): + current_app.logger.info('The captcha was not verified!') + return err(ERROR_UNVERIFIED_CAPTCHA) + else: + current_app.logger.info('Skipped captcha!') # We only allow POST data from certain origins allowed_origins = [v for k,v in current_app.config.items() if k.endswith('_ORIGIN')] origin = post_data.get('origin', 'NA') @@ -173,42 +173,42 @@ def post(self): 'channel': channel, 'icon_emoji': icon_emoji } - print email_body + # If we have an email body (should always be the case), send out the email -# if email_body: -# email_sent = False -# try: -# res = send_feedback_email(name, reply_to, post_data['_subject'], email_body, attachments=attachments) -# email_sent = True -# except Exception as e: -# current_app.logger.error('Fatal error while processing feedback form data: {0}'.format(e)) -# email_sent = False -# if not email_sent: -# # If the email could not be sent, we can still log the data submitted -# current_app.logger.error('Sending of email failed. Feedback data submitted by {0}: {1}'.format(post_data.get('email'), post_data)) -# return err(ERROR_EMAIL_NOT_SENT) -# # If we have Slack data, post the message to Slack -# if slack_data: -# slack_data['text'] += '\n*sent to adshelp*: {0}'.format(email_sent) -# try: -# slack_response = requests.post( -# url=current_app.config['FEEDBACK_SLACK_END_POINT'], -# data=json.dumps(slack_data), -# timeout=60 -# ) -# except (requests.exceptions.ConnectionError, requests.exceptions.Timeout): -# return b'504 Gateway Timeout', 504 -# current_app.logger.info('slack response: {0}' -# .format(slack_response.status_code)) -# -# # Slack annoyingly redirects if you have the wrong end point -# current_app.logger.info('Slack API' in slack_response.text) -# -# if 'Slack API' in slack_response.text: -# return err(ERROR_WRONG_ENDPOINT) -# elif slack_response.status_code == 200: -# return {}, 200 -# else: -# return {'msg': 'Unknown error'}, slack_response.status_code + if email_body: + email_sent = False + try: + res = send_feedback_email(name, reply_to, post_data['_subject'], email_body, attachments=attachments) + email_sent = True + except Exception as e: + current_app.logger.error('Fatal error while processing feedback form data: {0}'.format(e)) + email_sent = False + if not email_sent: + # If the email could not be sent, we can still log the data submitted + current_app.logger.error('Sending of email failed. Feedback data submitted by {0}: {1}'.format(post_data.get('email'), post_data)) + return err(ERROR_EMAIL_NOT_SENT) + # If we have Slack data, post the message to Slack + if slack_data: + slack_data['text'] += '\n*sent to adshelp*: {0}'.format(email_sent) + try: + slack_response = requests.post( + url=current_app.config['FEEDBACK_SLACK_END_POINT'], + data=json.dumps(slack_data), + timeout=60 + ) + except (requests.exceptions.ConnectionError, requests.exceptions.Timeout): + return b'504 Gateway Timeout', 504 + current_app.logger.info('slack response: {0}' + .format(slack_response.status_code)) + + # Slack annoyingly redirects if you have the wrong end point + current_app.logger.info('Slack API' in slack_response.text) + + if 'Slack API' in slack_response.text: + return err(ERROR_WRONG_ENDPOINT) + elif slack_response.status_code == 200: + return {}, 200 + else: + return {'msg': 'Unknown error'}, slack_response.status_code return {}, 200 From c28c21b3bb92f3cafdb7ee2111b73b0297921449 Mon Sep 17 00:00:00 2001 From: Edwin Henneken Date: Wed, 9 Dec 2020 07:54:16 -0500 Subject: [PATCH 3/4] fix for %A field in new abstract --- adsws/feedback/views.py | 3 +- adsws/tests/stubdata/new_abstract.py | 79 +--------------------------- 2 files changed, 3 insertions(+), 79 deletions(-) diff --git a/adsws/feedback/views.py b/adsws/feedback/views.py index c24cb50..9b800cc 100644 --- a/adsws/feedback/views.py +++ b/adsws/feedback/views.py @@ -90,7 +90,7 @@ def create_email_body(post_data): # We also attach the JSON data of the new record as a file if post_data.get('_subject') == 'New Record': try: - email_data['new']['author_list'] = ";".join([a['name'] for a in post_data['new']['authors']]) + email_data['new']['author_list'] = ";".join([a for a in post_data['new']['authors']]) except: email_data['new']['author_list'] = "" # Construct the email body @@ -173,7 +173,6 @@ def post(self): 'channel': channel, 'icon_emoji': icon_emoji } - # If we have an email body (should always be the case), send out the email if email_body: email_sent = False diff --git a/adsws/tests/stubdata/new_abstract.py b/adsws/tests/stubdata/new_abstract.py index 7587fea..8a89b94 100644 --- a/adsws/tests/stubdata/new_abstract.py +++ b/adsws/tests/stubdata/new_abstract.py @@ -1,79 +1,4 @@ # -*- coding: utf-8 -*- -data = { - "origin": "user_submission", - "g-recaptcha-response": "correct_response", - "_subject": "New Record", - "original": { - "entryType": "new", - "name": "", - "email": "", - "collection": [ - "astronomy" - ], - "bibcode": "", - "title": "", - "authors": [ - - ], - "publication": "", - "publicationDate": "", - "urls": [ - { - "value": "" - } - ], - "abstract": "", - "keywords": [ - { - "value": "" - } - ], - "references": [ - { - "value": "" - } - ], - "comments": "", - "recaptcha": "" - }, - "new": { - "collection": [ - "astronomy" - ], - "bibcode": "", - "title": "test", - "authors": [ - { - "id": "Shekh, S. H._0", - "position": 0, - "name": "Shekh, S. H.", - "aff": "Department of Mathematics, S. P. M. Science and Gilani Arts Commerece College Ghatanji, Maharashtra 445301, India", - "orcid": "" - }, - { - "id": "Shekh, S. H._0", - "position": 0, - "name": "Foo, Bar", - "aff": "Department of Mathematics, S. P. M. Science and Gilani Arts Commerece College Ghatanji, Maharashtra 445301, India", - "orcid": "" - }, - ], - "publication": "test", - "publicationDate": "2020-01", - "urls": [ - { - "type": "none", - "value": "" - } - ], - "abstract": "Present analysis dedicated to the dynamical investigation of anisotropic dark energy LRS Bianchi type-I cosmological model in the context of modified gravity in which Langrangian be the arbitrary function of Ricci scalar and Gouss-Bonnet invariant say f(R, G) gravity in the way of anisotropic fluid. The classification of the field equations towards f(R , G) =f0RmG 1 - m make available that the model is purely accelerating corresponds to 0 ~ q ~ - 1 . We govern the features of the derived cosmological model in view of the hybrid law inflation in bounce form (which involve power and de-Sitter cosmology) for the average scale factor. Also discussed the singularity of the model with the help of curvature of the model. It is observed that the model is fully engaged with both matter which exist initially for short expansion and dark energy dominated era and rests existing in quintessence dominated era and for sufficiently large time derived model forecasts that the anisotropy of the model will damp out and the Universe will turn out to be isotropic one and also observed that for quintessence dominated era the temperature and entropy density of anticipated model are positive definite along with some physical and kinematical parameters of the bounce model is also discussed in details.", - "keywords": [], - "references": [], - "comments": "test" - }, - "name": "Tim Hostetler", - "email": "twhostetler0@gmail.com", - "diff": "" -} +data = {u'origin': u'user_submission', u'g-recaptcha-response': u'03AGdBq247c2C8XzO4ks59M5v9TizvZfhtGWifTAJIK-QDawB-FxJFoo-cUqQEFJVnhsIGn4BD-aTvEJ-9dcQVrpVFZ9mZEmwjBE5_2_pos4ywHoVPnH9Pb-uXHvQRCp3mEKxr8Wu2IFezaVN4AJthktC7nNmmqtKN0_KQBHptqKjWOm44iLPwpMwRGBaQbFL9d8IgkAzROD6elDHLSq48cBCIUe3j_B0DDp6svuNyGF36FIpszmeLio0tqkvqcaoqSmmn_BgEe74iZId8kNaQy5PcZXNkNW-c9t9dRxE_infBwaBk5CwxjBdkgd_f09KziwMmDngyJj2RsAAF_RSxb6PzhFJimPu9HQOfqEuL3j4VR88bITbvq5s_n0In2uadRISlIQ_YIyRUw2-bBFe4thykKnpo5AC3AkVIhU65ab5pUlbt5VfQN1qb0dts-kwLDiGuQhtpIq9oNwwB848Fzj5FlzfOgBWgxg', u'email': u'cgrant@cfa.harvard.edu', u'_subject': u'New Record', u'diff': u'', u'new': {u'bibcode': u'2020DPS....5241201D', u'publication': u'AAS Division of Planetary Science meeting #52, id. 412.01. Bulletin of the American Astronomical Society, Vol. 52, No. 6 e-id 2020n6i412p01', u'title': u'tittle', u'abstract': u'wonderful new abstract', u'comments': u'user comments', u'collection': [u'astronomy'], u'affiliation': [u'KinetX Aerospace, Inc., Greenbelt, MD', u'IOTA, Greenbelt, MD', u'Southwest Research Institute, Boulder, CO', u'IOTA, Greenbelt, MD', u'IOTA, Murrumbateman', u'JPL, Pasadena, CA', u'JPL, Pasadena, CA', u'Planetary Exploration Res. Center, Chiba Inst. of Technology, Chiba Japan', u'CRAAG, Algiers Observatory, Algiers Algeria, MD'], u'references': [], u'publicationDate': u'2020-10-00', u'urls': [], u'authors': [u'Dunham, D.', u'Dunham, J.', u'Buie, M.', u'Preston, S.', u'Herald, D.', u'Farnocchia, D.', u'Giorgini, J.', u'Arai, T.', u'Aissa, B.'], u'keywords': [], u'orcid': [u'', u'', u'', u'', u'', u'', u'', u'', u'']}, u'original': {u'bibcode': u'2020DPS....5241201D', u'publication': u'AAS Division of Planetary Science meeting #52, id. 412.01. Bulletin of the American Astronomical Society, Vol. 52, No. 6 e-id 2020n6i412p01', u'title': u'tittle', u'abstract': u'wonderful new abstract', u'comments': u'user comments', u'collection': [u'astronomy'], u'affiliation': [u'KinetX Aerospace, Inc., Greenbelt, MD', u'IOTA, Greenbelt, MD', u'Southwest Research Institute, Boulder, CO', u'IOTA, Greenbelt, MD', u'IOTA, Murrumbateman', u'JPL, Pasadena, CA', u'JPL, Pasadena, CA', u'Planetary Exploration Res. Center, Chiba Inst. of Technology, Chiba Japan', u'CRAAG, Algiers Observatory, Algiers Algeria, MD'], u'references': [], u'publicationDate': u'2020-10-00', u'urls': [], u'authors': [u'Dunham, D.', u'Dunham, J.', u'Buie, M.', u'Preston, S.', u'Herald, D.', u'Farnocchia, D.', u'Giorgini, J.', u'Arai, T.', u'Aissa, B.'], u'keywords': [], u'orcid': [u'', u'', u'', u'', u'', u'', u'', u'', u'']}, u'name': u'Edw\xeen H\xe8nn\xebk\u0119n'} -response = 'From: Tim Hostetler\nAddress: twhostetler0@gmail.com\n\nAbstract data for new record. Summary:\n\nCollection: astronomy\n\n%R \n%T test\n%A Shekh, S. H.;Foo, Bar\n%D 2020-01\n%J test\n%B Present analysis dedicated to the dynamical investigation of anisotropic dark energy LRS Bianchi type-I cosmological model in the context of modified gravity in which Langrangian be the arbitrary function of Ricci scalar and Gouss-Bonnet invariant say f(R, G) gravity in the way of anisotropic fluid. The classification of the field equations towards f(R , G) =f0RmG 1 - m make available that the model is purely accelerating corresponds to 0 ~ q ~ - 1 . We govern the features of the derived cosmological model in view of the hybrid law inflation in bounce form (which involve power and de-Sitter cosmology) for the average scale factor. Also discussed the singularity of the model with the help of curvature of the model. It is observed that the model is fully engaged with both matter which exist initially for short expansion and dark energy dominated era and rests existing in quintessence dominated era and for sufficiently large time derived model forecasts that the anisotropy of the model will damp out and the Universe will turn out to be isotropic one and also observed that for quintessence dominated era the temperature and entropy density of anticipated model are positive definite along with some physical and kinematical parameters of the bounce model is also discussed in details.' \ No newline at end of file +response = u'From: Edw\xeen H\xe8nn\xebk\u0119n\nAddress: cgrant@cfa.harvard.edu\n\nAbstract data for new record. Summary:\n\nCollection: astronomy\n\n%R 2020DPS....5241201D\n%T tittle\n%A Dunham, D.;Dunham, J.;Buie, M.;Preston, S.;Herald, D.;Farnocchia, D.;Giorgini, J.;Arai, T.;Aissa, B.\n%D 2020-10-00\n%J AAS Division of Planetary Science meeting #52, id. 412.01. Bulletin of the American Astronomical Society, Vol. 52, No. 6 e-id 2020n6i412p01\n%B wonderful new abstract' \ No newline at end of file From 96efb7efcd394da71885240af8ae3a59bcdc677a Mon Sep 17 00:00:00 2001 From: Edwin Henneken Date: Wed, 9 Dec 2020 10:24:23 -0500 Subject: [PATCH 4/4] stubdata fix for failing unittest --- adsws/tests/stubdata/new_abstract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adsws/tests/stubdata/new_abstract.py b/adsws/tests/stubdata/new_abstract.py index 8a89b94..72efa33 100644 --- a/adsws/tests/stubdata/new_abstract.py +++ b/adsws/tests/stubdata/new_abstract.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -data = {u'origin': u'user_submission', u'g-recaptcha-response': u'03AGdBq247c2C8XzO4ks59M5v9TizvZfhtGWifTAJIK-QDawB-FxJFoo-cUqQEFJVnhsIGn4BD-aTvEJ-9dcQVrpVFZ9mZEmwjBE5_2_pos4ywHoVPnH9Pb-uXHvQRCp3mEKxr8Wu2IFezaVN4AJthktC7nNmmqtKN0_KQBHptqKjWOm44iLPwpMwRGBaQbFL9d8IgkAzROD6elDHLSq48cBCIUe3j_B0DDp6svuNyGF36FIpszmeLio0tqkvqcaoqSmmn_BgEe74iZId8kNaQy5PcZXNkNW-c9t9dRxE_infBwaBk5CwxjBdkgd_f09KziwMmDngyJj2RsAAF_RSxb6PzhFJimPu9HQOfqEuL3j4VR88bITbvq5s_n0In2uadRISlIQ_YIyRUw2-bBFe4thykKnpo5AC3AkVIhU65ab5pUlbt5VfQN1qb0dts-kwLDiGuQhtpIq9oNwwB848Fzj5FlzfOgBWgxg', u'email': u'cgrant@cfa.harvard.edu', u'_subject': u'New Record', u'diff': u'', u'new': {u'bibcode': u'2020DPS....5241201D', u'publication': u'AAS Division of Planetary Science meeting #52, id. 412.01. Bulletin of the American Astronomical Society, Vol. 52, No. 6 e-id 2020n6i412p01', u'title': u'tittle', u'abstract': u'wonderful new abstract', u'comments': u'user comments', u'collection': [u'astronomy'], u'affiliation': [u'KinetX Aerospace, Inc., Greenbelt, MD', u'IOTA, Greenbelt, MD', u'Southwest Research Institute, Boulder, CO', u'IOTA, Greenbelt, MD', u'IOTA, Murrumbateman', u'JPL, Pasadena, CA', u'JPL, Pasadena, CA', u'Planetary Exploration Res. Center, Chiba Inst. of Technology, Chiba Japan', u'CRAAG, Algiers Observatory, Algiers Algeria, MD'], u'references': [], u'publicationDate': u'2020-10-00', u'urls': [], u'authors': [u'Dunham, D.', u'Dunham, J.', u'Buie, M.', u'Preston, S.', u'Herald, D.', u'Farnocchia, D.', u'Giorgini, J.', u'Arai, T.', u'Aissa, B.'], u'keywords': [], u'orcid': [u'', u'', u'', u'', u'', u'', u'', u'', u'']}, u'original': {u'bibcode': u'2020DPS....5241201D', u'publication': u'AAS Division of Planetary Science meeting #52, id. 412.01. Bulletin of the American Astronomical Society, Vol. 52, No. 6 e-id 2020n6i412p01', u'title': u'tittle', u'abstract': u'wonderful new abstract', u'comments': u'user comments', u'collection': [u'astronomy'], u'affiliation': [u'KinetX Aerospace, Inc., Greenbelt, MD', u'IOTA, Greenbelt, MD', u'Southwest Research Institute, Boulder, CO', u'IOTA, Greenbelt, MD', u'IOTA, Murrumbateman', u'JPL, Pasadena, CA', u'JPL, Pasadena, CA', u'Planetary Exploration Res. Center, Chiba Inst. of Technology, Chiba Japan', u'CRAAG, Algiers Observatory, Algiers Algeria, MD'], u'references': [], u'publicationDate': u'2020-10-00', u'urls': [], u'authors': [u'Dunham, D.', u'Dunham, J.', u'Buie, M.', u'Preston, S.', u'Herald, D.', u'Farnocchia, D.', u'Giorgini, J.', u'Arai, T.', u'Aissa, B.'], u'keywords': [], u'orcid': [u'', u'', u'', u'', u'', u'', u'', u'', u'']}, u'name': u'Edw\xeen H\xe8nn\xebk\u0119n'} +data = {u'origin': u'user_submission', u'g-recaptcha-response': u'correct_response', u'email': u'cgrant@cfa.harvard.edu', u'_subject': u'New Record', u'diff': u'', u'new': {u'bibcode': u'2020DPS....5241201D', u'publication': u'AAS Division of Planetary Science meeting #52, id. 412.01. Bulletin of the American Astronomical Society, Vol. 52, No. 6 e-id 2020n6i412p01', u'title': u'tittle', u'abstract': u'wonderful new abstract', u'comments': u'user comments', u'collection': [u'astronomy'], u'affiliation': [u'KinetX Aerospace, Inc., Greenbelt, MD', u'IOTA, Greenbelt, MD', u'Southwest Research Institute, Boulder, CO', u'IOTA, Greenbelt, MD', u'IOTA, Murrumbateman', u'JPL, Pasadena, CA', u'JPL, Pasadena, CA', u'Planetary Exploration Res. Center, Chiba Inst. of Technology, Chiba Japan', u'CRAAG, Algiers Observatory, Algiers Algeria, MD'], u'references': [], u'publicationDate': u'2020-10-00', u'urls': [], u'authors': [u'Dunham, D.', u'Dunham, J.', u'Buie, M.', u'Preston, S.', u'Herald, D.', u'Farnocchia, D.', u'Giorgini, J.', u'Arai, T.', u'Aissa, B.'], u'keywords': [], u'orcid': [u'', u'', u'', u'', u'', u'', u'', u'', u'']}, u'original': {u'bibcode': u'2020DPS....5241201D', u'publication': u'AAS Division of Planetary Science meeting #52, id. 412.01. Bulletin of the American Astronomical Society, Vol. 52, No. 6 e-id 2020n6i412p01', u'title': u'tittle', u'abstract': u'wonderful new abstract', u'comments': u'user comments', u'collection': [u'astronomy'], u'affiliation': [u'KinetX Aerospace, Inc., Greenbelt, MD', u'IOTA, Greenbelt, MD', u'Southwest Research Institute, Boulder, CO', u'IOTA, Greenbelt, MD', u'IOTA, Murrumbateman', u'JPL, Pasadena, CA', u'JPL, Pasadena, CA', u'Planetary Exploration Res. Center, Chiba Inst. of Technology, Chiba Japan', u'CRAAG, Algiers Observatory, Algiers Algeria, MD'], u'references': [], u'publicationDate': u'2020-10-00', u'urls': [], u'authors': [u'Dunham, D.', u'Dunham, J.', u'Buie, M.', u'Preston, S.', u'Herald, D.', u'Farnocchia, D.', u'Giorgini, J.', u'Arai, T.', u'Aissa, B.'], u'keywords': [], u'orcid': [u'', u'', u'', u'', u'', u'', u'', u'', u'']}, u'name': u'Edw\xeen H\xe8nn\xebk\u0119n'} response = u'From: Edw\xeen H\xe8nn\xebk\u0119n\nAddress: cgrant@cfa.harvard.edu\n\nAbstract data for new record. Summary:\n\nCollection: astronomy\n\n%R 2020DPS....5241201D\n%T tittle\n%A Dunham, D.;Dunham, J.;Buie, M.;Preston, S.;Herald, D.;Farnocchia, D.;Giorgini, J.;Arai, T.;Aissa, B.\n%D 2020-10-00\n%J AAS Division of Planetary Science meeting #52, id. 412.01. Bulletin of the American Astronomical Society, Vol. 52, No. 6 e-id 2020n6i412p01\n%B wonderful new abstract' \ No newline at end of file