Source code for ldsnotes.note

import requests
from time import sleep
from ldsnotes.annotations import make_annotation
from addict import Dict
from datetime import datetime

# install chrome driver
import chromedriver_autoinstaller
# basic selenium imports
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
# used for waiting for pages to load
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

TAGS = "https://www.churchofjesuschrist.org/notes/api/v2/tags"
ANNOTATIONS = "https://www.churchofjesuschrist.org/notes/api/v2/annotations"
FOLDERS = "https://www.churchofjesuschrist.org/notes/api/v2/folders"


[docs]class Tag(Dict): """Object that holds all Tag info Attributes ----------- annotationCount : string Number of annotations assigned to tag name : string Name of tag id : string Seems to always be the same as name... lastUsed : datetime Last time that tag was edited""" def __init__(self, *args, **kwargs): super().__init__(self, *args, **kwargs) self.lastUsed = datetime.fromisoformat(self.lastUsed) def __str__(self): return "(Tag) " + self.name __repr__ = __str__
[docs]class Folder(Dict): """Object that holds all Tag info Attributes ----------- annotationCount : string Number of annotations in folder name : string Name of folder id : string Special id of folder lastUsed : datetime Last time that the folder was changed order.id : list List of note ids in order that they were put in.""" def __init__(self, *args, **kwargs): super().__init__(self, *args, **kwargs) def __str__(self): return "(Folder) " + self.name __repr__ = __str__
[docs]class Notes: """Wrapper to pull any annotations from lds.org. The API is rather complex to use to login. We take the lazy route and login with selenium (basically a fake browser). To avoid doing this everytime you can save the necessary token. The object also supports indexing, so you can get the first note with n[0], or the first 10 doing n[:10]. When doing this it'll return the most recently edited objects. Whenever querying, it'll always return the most recently edited objects. Parameters ----------- username : string Your username password : string Your password token : string Instead of inputting your username/password, you can save your token and just input it headless : bool Whether to run selenium headless or not Attributes ----------- tags : list List of Tag objects of all your tags folders : list List of Folder objects of all your folders""" def __init__(self, username=None, password=None, token=None, headless=True): self.session = requests.Session() if token is None: self.username = username self.password = password self._login(headless) else: self.token = token self.session.cookies.set("oauth_id_token", self.token) def _login(self, headless): # install chromedriver chromedriver_autoinstaller.install() # run headless options = Options() if headless: options.add_argument("--headless") options.add_argument("--window-size=1920x1080") browser = webdriver.Chrome(options=options) # login using selenium browser.get("https://churchofjesuschrist.org/notes") # username page login = WebDriverWait(browser, 10).until( EC.presence_of_element_located((By.NAME, "username")) ) login.clear() login.send_keys(self.username) login.send_keys(Keys.RETURN) # password page auth = WebDriverWait(browser, 10).until( EC.presence_of_element_located((By.NAME, "password")) ) auth.clear() auth.send_keys(self.password) auth.send_keys(Keys.RETURN) sleep(3) # copy over cookies into our request session self.token = [c['value'] for c in browser.get_cookies( ) if c['name'] == "oauth_id_token"][0] self.session.cookies.set("oauth_id_token", self.token) return self.token @property def tags(self): return [Tag(t) for t in self.session.get(url=TAGS).json()] @property def folders(self): return [Folder(f) for f in self.session.get(url=FOLDERS).json()] def __getitem__(self, val): if isinstance(val, slice): if val.start is None: start = 0 else: start = val.start num = val.stop - start # api indexes at 1 start += 1 elif isinstance(val, int): start = val + 1 num = 1 params = {"start": start, "numberToReturn": num, "notesAsHtml": False} return make_annotation(self.session.get( url=ANNOTATIONS, params=params).json()) def search(self, keyword=None, tag=None, folder=None, annot_type=["bookmark", "highlight", "journal", "reference"], start=1, stop=51, as_html=False, json=False): """Searches for annotations. Parameters ----------- keyword : string Keyword to search for. Defaults to None. tag : string Name of tag you want to search for. Defaults to None. folder : string Name of folder you want to search for. Defaults to None. annot_type : list/string Type of annotation to pull. Can be a list/one of bookmark, highlight, journal, reference. Defaults to all of them. start : int How deep in to start search (must be > 1). Defaults to 1. stop : int Where to stop search. Defaults to 51. as_html : bool If True, returns notes with html tags. If False, returns as markdown (I think). Defaults to False. json : bool If True, returns raw data from lds.org. If False, returns our cleaned objects. Returns -------- List of strings or Bookmark/Highlight/Journal/Reference objects """ # clean out requested annotation type if isinstance(annot_type, str): annot_type = [annot_type] types = ["highlight", "journal", "reference", "bookmark"] bad = [t for t in annot_type if t not in types] if len(bad) != 0: raise ValueError("You tried to search for type that doesn't exist") # setup request params = { "start": start, "numberToReturn": stop - start, "notesAsHtml": as_html} params['type'] = ",".join(annot_type) if tag is not None: params['tags'] = tag if folder is not None: folderid = [f.id for f in self.folders if f.name == folder][0] params['folderId'] = folderid if keyword is not None: params['searchPhrase'] = keyword # send request if json: return self.session.get(url=ANNOTATIONS, params=params).json() else: return make_annotation(self.session.get( url=ANNOTATIONS, params=params).json())