docs: def get_pubmed(pmid: str) -> Mapping[str, Any]:
      """Get pubmed xml for pmid and convert to JSON
  
      Remove MESH terms if they are duplicated in the compound term set
  
      ArticleDate vs PubDate gets complicated: https://www.nlm.nih.gov/bsd/licensee/elements_descriptions.html see <ArticleDate> and <PubDate>
      Only getting pub_year at this point from the <PubDate> element.
  
      Args:
          pmid: pubmed id number as a string
  
      Returns:
          pubmed json
      """
      pubmed_url = PUBMED_TMPL.replace("PMID", str(pmid))
      r = get_url(pubmed_url)
      log.info(f"Getting Pubmed URL {pubmed_url}")
  
      try:
          root = etree.fromstring(r.content)
          doc = {"abstract": ""}
          doc["pmid"] = root.xpath("//PMID/text()")[0]
          doc["title"] = next(iter(root.xpath("//ArticleTitle/text()")), "")
  
          # TODO https://stackoverflow.com/questions/4770191/lxml-etree-element-text-doesnt-return-the-entire-text-from-an-element
          atext = next(iter(root.xpath("//Abstract/AbstractText/text()")), "")
          print("Text", atext)
  
          for abstracttext in root.xpath("//Abstract/AbstractText"):
              abstext = node_text(abstracttext)
  
              label = abstracttext.get("Label", None)
              if label:
                  doc["abstract"] += f"{label}: {abstext}\n"
              else:
                  doc["abstract"] += f"{abstext}\n"
  
          doc["abstract"] = doc["abstract"].rstrip()
  
          doc["authors"] = []
          for author in root.xpath("//Author"):
              last_name = next(iter(author.xpath("LastName/text()")), "")
              first_name = next(iter(author.xpath("ForeName/text()")), "")
              initials = next(iter(author.xpath("Initials/text()")), "")
              if not first_name and initials:
                  first_name = initials
              doc["authors"].append(f"{last_name}, {first_name}")
  
          pub_year = next(
              iter(root.xpath("//Journal/JournalIssue/PubDate/Year/text()")), None
          )
          pub_mon = next(
              iter(root.xpath("//Journal/JournalIssue/PubDate/Month/text()")), "Jan"
          )
          pub_day = next(
              iter(root.xpath("//Journal/JournalIssue/PubDate/Day/text()")), "01"
          )
  
          pub_date = process_pub_date(pub_year, pub_mon, pub_day)
  
          doc["pub_date"] = pub_date
          doc["journal_title"] = next(iter(root.xpath("//Journal/Title/text()")), "")
          doc["joural_iso_title"] = next(
              iter(root.xpath("//Journal/ISOAbbreviation/text()")), ""
          )
          doc["doi"] = next(iter(root.xpath('//ArticleId[@IdType="doi"]/text()')), None)
  
          doc["compounds"] = []
          for chem in root.xpath("//ChemicalList/Chemical/NameOfSubstance"):
              chem_id = chem.get("UI")
              doc["compounds"].append({"id": f"MESH:{chem_id}", "name": chem.text})
  
          compounds = [cmpd["id"] for cmpd in doc["compounds"]]
          doc["mesh"] = []
          for mesh in root.xpath("//MeshHeading/DescriptorName"):
              mesh_id = f"MESH:{mesh.get('UI')}"
              if mesh_id in compounds:
                  continue
              doc["mesh"].append({"id": mesh_id, "name": mesh.text})
  
          return doc
      except Exception as e:
          log.error(
              f"Bad Pubmed request, status: {r.status_code} error: {e}",
              url=f'{PUBMED_TMPL.replace("PMID", pmid)}',
          )
          return {"message": f"Cannot get PMID: {pubmed_url}"}