From ef50c3da3d6e723b76098bfd0109197e56fc22c2 Mon Sep 17 00:00:00 2001 From: Paco Nathan Date: Tue, 11 Feb 2020 09:53:30 -0800 Subject: [PATCH 1/2] preparations for v1.1.1 release --- README.md | 10 +- changelog.txt | 2 +- docs/enrich_pubs.ipynb | 200 -------------- docs/get_api_md.ipynb | 582 ----------------------------------------- setup.py | 4 +- 5 files changed, 8 insertions(+), 790 deletions(-) delete mode 100644 docs/enrich_pubs.ipynb delete mode 100644 docs/get_api_md.ipynb diff --git a/README.md b/README.md index 92eca41..05e7728 100644 --- a/README.md +++ b/README.md @@ -96,9 +96,6 @@ else: source.report_perf(timing) ``` -Check out the unit test code in `test.py` for more usage patterns per -supported API. - ## API Integrations @@ -123,8 +120,11 @@ APIs used to retrieve metadata: + [Dimensions](https://docs.dimensions.ai/dsl/api.html) + [SSRN](https://www.ssrn.com/) -See `docs/enrich_pubs.ipynb` for example API usage to pull the -federated metadata for a publication. +See the coding examples in the `test.py` unit test for usage patterns +per supported API. + + +## Literature For more background about *open access publications* see: diff --git a/changelog.txt b/changelog.txt index 87f9310..97d5765 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,7 +2,7 @@ ## 1.1.1 -2020-02-?? +2020-02-11 - added `has_credentials()` test for config settings for each API diff --git a/docs/enrich_pubs.ipynb b/docs/enrich_pubs.ipynb deleted file mode 100644 index 1a8b677..0000000 --- a/docs/enrich_pubs.ipynb +++ /dev/null @@ -1,200 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import RichContextAPI\n", - "import importlib\n", - "import json\n", - "import time\n", - "import os\n", - "importlib.reload(RichContextAPI)" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "def get_publication_metadata(p):\n", - " title = p['title'].lower()\n", - " p.update({'title':title})\n", - " try:\n", - " epmc_md = RichContextAPI.get_epmc_md(title)\n", - " p.update({'europepmc':epmc_md})\n", - " print('found metadata in europe pmc')\n", - " except:\n", - " pass\n", - " try:\n", - " oa_md = RichContextAPI.oa_lookup_pub_uris(title)\n", - " p.update({'openaire':oa_md})\n", - " print('found metadata in openaire')\n", - " except:\n", - " pass\n", - " try:\n", - " ssrn_md = RichContextAPI.get_ssrn_md(title)\n", - " p.update({'ssrn':ssrn_md})\n", - " print('found metadata in ssrn')\n", - " except:\n", - " pass\n", - " try:\n", - " dimensions_md = RichContextAPI.get_dimensions_md(title)\n", - " p.update({'dimensions':dimensions_md})\n", - " print('found metadata in dimensions')\n", - " except:\n", - " pass\n", - " return p" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [], - "source": [ - "pub_path = '/Users/sophierand/RCPublications/partitions/20190610_usda_iri_publications.json'\n", - "with open(pub_path) as json_file:\n", - " publications = json.load(json_file)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "found metadata in europe pmc\n", - "found metadata in openaire\n", - "found metadata in dimensions\n" - ] - } - ], - "source": [ - "p = publications[0]\n", - "pub_md = get_publication_metadata(p)" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'url': {'url': 'http://europepmc.org/articles/PMC4427330',\n", - " 'pdf': 'http://europepmc.org/articles/PMC4427330?pdf=render'},\n", - " 'journal': 'PLoS ONE',\n", - " 'title': 'relationships between diet, alcohol preference, and heart disease and type 2 diabetes among americans',\n", - " 'doi': '10.1371/journal.pone.0124351'}" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pub_md['openaire']" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [], - "source": [ - "md_path = \"/Users/sophierand/RCCustomers/exports/USDA/pub_md.json\"" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [], - "source": [ - "with open(md_path, 'w') as outfile:\n", - " json.dump(pub_md, outfile,indent = 2)" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [], - "source": [ - "# oa_md = RichContextAPI.oa_lookup_pub_uris(title)" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'journal': 'PLoS ONE',\n", - " 'title': 'relationships between diet, alcohol preference, and heart disease and type 2 diabetes among americans',\n", - " 'doi': '10.1371/journal.pone.0124351',\n", - " 'url': 'http://europepmc.org/articles/PMC4427330',\n", - " 'pdf': 'http://europepmc.org/articles/PMC4427330?pdf=render'}" - ] - }, - "execution_count": 47, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# oa_md" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/get_api_md.ipynb b/docs/get_api_md.ipynb deleted file mode 100644 index fa2fa9e..0000000 --- a/docs/get_api_md.ipynb +++ /dev/null @@ -1,582 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Generating publication metadata with APIs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Dimensions md" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NbConvertApp] Converting notebook get_api_md.ipynb to script\n", - "[NbConvertApp] Writing 2190 bytes to get_api_md.py\n" - ] - } - ], - "source": [ - "!jupyter nbconvert --to script get_api_md.ipynb" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import RichContextAPI\n" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "import configparser\n", - "import dimensions_search_api_client as dscli" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "def connect_ds_api(username,password):\n", - " api_client = dscli.DimensionsSearchAPIClient()\n", - " api_client.set_max_in_items( 100 )\n", - " api_client.set_max_return( 1000 )\n", - " api_client.set_max_overall_returns( 50000 )\n", - " api_client.set_username( username )\n", - " api_client.set_password( password )\n", - " return api_client\n", - "\n", - "def connect_dimensions_api():\n", - " CONFIG = configparser.ConfigParser()\n", - " CONFIG.read(\"richcontext_config.cfg\")\n", - " api_client = connect_ds_api(username= CONFIG.get('DEFAULT','username'),password = CONFIG.get('DEFAULT','password'))\n", - " return api_client" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "def dimensions_title_test():\n", - " title = 'Relationships between Diet, Alcohol Preference, and Heart Disease and Type 2 Diabetes among Americans'\n", - " query = 'search publications in title_only for \"\\\\\"{}\\\\\"\" return publications[all]'.format(title)\n", - " dimensions_return = api_client.execute_query(query_string_IN = query )\n", - " if dimensions_return:\n", - " title_return = dimensions_return['publications']\n", - " if len(title_return) > 0:\n", - " print('passed test')\n", - " if not dimensions_return:\n", - " print('failed')\n", - " \n" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "api_client = connect_dimensions_api()" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "passed test\n" - ] - } - ], - "source": [ - "dimensions_title_test()" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [], - "source": [ - "import urllib\n", - "import requests\n", - "from bs4 import BeautifulSoup\n", - "\n", - "def gen_empc_url(title):\n", - " epmc_url = 'http://europepmc.org/search?query=' + urllib.parse.quote(title)\n", - " return epmc_url\n", - "\n", - "def get_europepmc_metadata (url):\n", - " \"\"\"\n", - " parse metadata from a Europe PMC web page for a publication\n", - " \"\"\"\n", - "\n", - " response = requests.get(url).text\n", - "\n", - " publisher = None\n", - " doi = None\n", - " pdf = None\n", - " new_url = None\n", - "\n", - " soup = BeautifulSoup(response, \"html.parser\")\n", - "\n", - "\n", - " publisher_list_pmcmata = soup.find_all(\"span\", {\"id\": \"pmcmata\"})\n", - " if len(publisher_list_pmcmata) > 0:\n", - " for x in publisher_list_pmcmata:\n", - " publisher = x.get_text()\n", - " if len(publisher_list_pmcmata) == 0:\n", - " publisher_list_citation = soup.find_all(\"meta\", {\"name\": \"citation_journal_abbrev\"})\n", - " if len(publisher_list_citation) > 0:\n", - " for x in publisher_list_citation:\n", - " publisher = x['content']\n", - " for x in soup.find_all(\"meta\", {\"name\": \"citation_title\"}):\n", - " title = x['content']\n", - "\n", - " for x in soup.find_all(\"meta\", {\"name\": \"citation_doi\"}):\n", - " doi = x[\"content\"]\n", - "\n", - " for x in soup.find_all(\"meta\", {\"name\": \"citation_pdf_url\"}):\n", - " pdf = x[\"content\"]\n", - "\n", - " for x in soup.find_all(\"a\", {\"class\": \"abs_publisher_link\"}):\n", - " new_url = x['href']\n", - "\n", - " if title:\n", - " epmc_data = {'title':title}\n", - " if doi:\n", - " epmc_data.update({'doi':doi})\n", - " if publisher:\n", - " epmc_data.update({'journal':publisher})\n", - " if pdf:\n", - " epmc_data.update({'pdf':pdf})\n", - " if new_url:\n", - " epmc_data.update({'url':new_url})\n", - " return epmc_data\n", - " else:\n", - " return None\n", - " \n", - " \n" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [], - "source": [ - "url = \"http://europepmc.org/abstract/MED/20195444\"" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [], - "source": [ - "epmc_md = get_europepmc_metadata(url)" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'title': 'Categorizing US state drinking practices and consumption trends.',\n", - " 'doi': '10.3390/ijerph7010269',\n", - " 'journal': 'Int J Environ Res Public Health',\n", - " 'url': 'https://doi.org/10.3390/ijerph7010269'}" - ] - }, - "execution_count": 49, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "epmc_md" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "title = 'Relationships between Diet, Alcohol Preference, and Heart Disease and Type 2 Diabetes among Americans'\n", - "dimensions_md = RichContextAPI.get_dimensions_md(title)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "dict_keys(['linkout', 'authors', 'doi', 'keywords', 'journal_title', 'title'])" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dimensions_md.keys()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### SSRN md" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "title = 'Modeling the Term Structure from the On-the-Run Treasury Yield Curve'\n", - "ssrn_md = RichContextAPI.get_ssrn_md(title)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Europe PMC" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import RichContextAPI\n", - "title = \"Categorizing US State Drinking Practices and Consumption Trends\"\n", - "page_data = RichContextAPI.get_epmc_md(title)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### OpenAire" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "import RichContextAPI\n", - "title = \"Categorizing US State Drinking Practices and Consumption Trends\"\n", - "oa_d = RichContextAPI.oa_lookup_pub_uris(title)" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": {}, - "outputs": [], - "source": [ - "from urllib import parse\n", - "import xml.etree.ElementTree as et\n", - "\n", - "def oa_load_uri (uri):\n", - " with urllib.request.urlopen(uri) as response:\n", - " html = response.read()\n", - " return html.decode(\"utf-8\")\n", - " \n", - "\n", - "API_URI = \"http://api.openaire.eu/search/publications?title=\"\n", - "\n", - "def oa_lookup_pub_uris (title):\n", - " xml = oa_load_uri(API_URI + parse.quote(title))\n", - " pub_url = oa_extract_pub_uri(xml)\n", - " journal = oa_extract_journal(xml)\n", - " doi = oa_extract_doi(xml)\n", - "\n", - " if pub_url:\n", - " oa_dict = {'journal':journal,'title':title,'doi':doi}\n", - " oa_dict.update(pub_url)\n", - " return oa_dict\n", - " if not pub_url:\n", - " return None\n", - " \n", - " \n", - "\n", - "\n", - "NS = {\n", - " \"oaf\": \"http://namespace.openaire.eu/oaf\"\n", - " }\n", - "\n", - "def oa_extract_pub_uri (xml):\n", - " root = et.fromstring(xml)\n", - " result = root.findall(\"./results/result[1]/metadata/oaf:entity/oaf:result\", NS)\n", - "\n", - " if len(result) > 0:\n", - " url_list = result[0].findall(\"./children/instance/webresource/url\")\n", - "\n", - " if len(url_list) > 0:\n", - " url_list_text = [u.text for u in url_list]\n", - " pdf = [p for p in url_list_text if 'pdf' in p]\n", - " url = [u for u in url_list_text if u not in pdf and 'europepmc' in u]\n", - " url_dict = {}\n", - " if len(url) > 0:\n", - " url_dict.update({'url':url[0]})\n", - " if len(pdf) > 0:\n", - " url_dict.update({'pdf':pdf[0]})\n", - " return url_dict\n", - "\n", - " return None\n", - "\n", - "def oa_extract_publisher (xml):\n", - " root = et.fromstring(xml)\n", - " result = root.findall(\"./results/result[1]/metadata/oaf:entity/oaf:result\", NS)\n", - " if len(result) > 0:\n", - " publisher_list = result[0].findall(\"./collectedfrom\")\n", - " if len(publisher_list) > 0:\n", - " publisher_name = publisher_list[0].attrib['name']\n", - " return publisher_name\n", - " elif len(result) == 0:\n", - " return None\n", - " \n", - " \n", - "def oa_extract_doi (xml):\n", - " root = et.fromstring(xml)\n", - " result = root.findall(\"./results/result[1]/metadata/oaf:entity/oaf:result\", NS)\n", - " if len(result) > 0:\n", - " doi = result[0].find(\"./pid[@classid='doi']\")\n", - " if doi is not None:\n", - " doi = doi.text\n", - " return doi\n", - "\n", - "def oa_extract_journal (xml):\n", - " root = et.fromstring(xml)\n", - " result = root.findall(\"./results/result[1]/metadata/oaf:entity/oaf:result\", NS)\n", - " if len(result) > 0:\n", - " journal = result[0].find(\"./journal\")\n", - " if journal is not None:\n", - " journal_name = journal.text\n", - " return journal_name" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": {}, - "outputs": [], - "source": [ - "title = \"Categorizing US State Drinking Practices and Consumption Trends\"\n", - "oa_d = oa_lookup_pub_uris(title)" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'journal': 'International Journal of Environmental Research and Public Health',\n", - " 'title': 'Categorizing US State Drinking Practices and Consumption Trends',\n", - " 'doi': '10.3390/ijerph7010269',\n", - " 'url': 'http://europepmc.org/articles/PMC2819787'}" - ] - }, - "execution_count": 58, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "oa_d" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Running publications through APIs" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import RichContextAPI\n", - "import importlib\n", - "import json\n", - "import time\n", - "import os\n", - "importlib.reload(RichContextAPI)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "pub_path = '/Users/sophierand/RCPublications/partitions/20190610_usda_iri_publications.json'" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "with open(pub_path) as json_file:\n", - " publications = json.load(json_file)" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "p = publications[0]\n", - "# p" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "found metadata in europe pmc\n", - "found metadata in openaire\n", - "found metadata in dimensions\n" - ] - } - ], - "source": [ - "def get_publication_metadata(p):\n", - " title = p['title']\n", - " try:\n", - " epmc_md = RichContextAPI.get_epmc_md(title)\n", - " p.update({'europepmc':epmc_md})\n", - " print('found metadata in europe pmc')\n", - " except:\n", - " pass\n", - " try:\n", - " oa_md = RichContextAPI.oa_lookup_pub_uris(title)\n", - " p.update({'openaire':oa_md})\n", - " print('found metadata in openaire')\n", - " except:\n", - " pass\n", - " try:\n", - " ssrn_md = RichContextAPI.get_ssrn_md(title)\n", - " p.update({'ssrn':ssrn_md})\n", - " print('found metadata in ssrn')\n", - " except:\n", - " pass\n", - " try:\n", - " dimensions_md = RichContextAPI.get_dimensions_md(title)\n", - " p.update({'dimensions':dimensions_md})\n", - " print('found metadata in dimensions')\n", - " except:\n", - " pass\n", - " return p" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "# p.keys()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# epmc_md = RichContextAPI.get_epmc_md(title)\n", - "# oa_md = RichContextAPI.oa_lookup_pub_uris(title)\n", - "# ssrn_md = RichContextAPI.get_ssrn_md(title)\n", - "# dimensions_md = RichContextAPI.get_dimensions_md(title)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/setup.py b/setup.py index c8b370b..7b1c35b 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="richcontext-scholapi", - version="1.1.0", + version="1.1.1", author="Coleridge Initiative", author_email="dataanalytics@coleridgeinitiative.org", description="Rich Context API integrations for federating discovery services and metadata exchange across multiple scholarly infrastructure providers", @@ -37,7 +37,7 @@ "selenium", "xmltodict", ], - keywords="Rich Context, DOI, ISSN, ROR, ORCID, RePEc, PubMed, Crossref, Dimensions, EuropePMC, OpenAIRE, Unpaywall, dissemin, Semantic Scholar, discovery, discovery service, federated API, federated metadata, knowledge graph, metadata API, metadata exchange, metadata, persistent identifiers, research publication ontology, research publications, scholarly infrastructure, scholarly metadata, scholarly publishing", + keywords="Rich Context, DOI, ISSN, ROR, ORCID, PubMed, EuropePMC, Scholix, Crossref, DataCide, OpenAIRE, Unpaywall, dissemin, Semantic Scholar, RePEc, Dimensions, discovery, discovery service, federated API, federated metadata, knowledge graph, metadata API, metadata exchange, metadata, persistent identifiers, research publication ontology, research publications, scholarly infrastructure, scholarly metadata, scholarly publishing", license="MIT", include_package_data=True, zip_safe=False From 918f7eabcce5b4b85a6897269569bef181b29c56 Mon Sep 17 00:00:00 2001 From: Paco Nathan Date: Tue, 11 Feb 2020 09:55:42 -0800 Subject: [PATCH 2/2] WIP a response class --- richcontext/scholapi/scholapi.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/richcontext/scholapi/scholapi.py b/richcontext/scholapi/scholapi.py index 337f251..d2deef0 100755 --- a/richcontext/scholapi/scholapi.py +++ b/richcontext/scholapi/scholapi.py @@ -1039,8 +1039,11 @@ class _ScholInfraResponse: manage the response from a specific Scholarly Infrastructure API """ - def __init__ (self): + def __init__ (self, parent=None, meta=None, timing=0, message=None): + self.parent = parent self.meta = meta + self.timing = timing + self.message = message ######################################################################