Skip to content

Commit

Permalink
refactor: Unify naming simplenote variables & decouple from config.py
Browse files Browse the repository at this point in the history
  • Loading branch information
RedAtman committed Jun 25, 2024
1 parent 8bcf1b0 commit 21da7b6
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 116 deletions.
24 changes: 14 additions & 10 deletions _config.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def __init_subclass__(cls, **kwargs):
PROJECT_VERSION: str = "0.0.1"
PROJECT_DESCRIPTION: str = "Simplenote"

SIMPLENOTE_DIR: str = BASE_DIR
SETTINGS_FILE: str = "simplenote.sublime-settings"
SIMPLENOTE_APP_ID: str = os.getenv("SIMPLENOTE_APP_ID", "chalk-bump-f49")
__SIMPLENOTE_APP_KEY: str = os.getenv("SIMPLENOTE_APP_KEY", "YzhjMmI4NjMzNzE1NGNkYWJjOTg5YjIzZTMwYzZiZjQ=")
Expand All @@ -51,18 +52,14 @@ def __init_subclass__(cls, **kwargs):
# SIMPLENOTE_APP_KEY: bytes = base64.b64decode(__SIMPLENOTE_APP_KEY)
SIMPLENOTE_APP_KEY: str = base64.b64decode("YzhjMmI4NjMzNzE1NGNkYWJjOTg5YjIzZTMwYzZiZjQ=").decode("utf-8")
SIMPLENOTE_BUCKET: str = os.getenv("SIMPLENOTE_BUCKET", "note")
SIMPLENOTE_AUTH_URL: str = f"https://auth.simperium.com/1/{SIMPLENOTE_APP_ID}/authorize/"
SIMPLENOTE_DATA_URL: str = f"https://api.simperium.com/1/{SIMPLENOTE_APP_ID}/{SIMPLENOTE_BUCKET}"
SIMPLENOTE_USERNAME: str = os.getenv("SIMPLENOTE_USERNAME", "")
SIMPLENOTE_PASSWORD: str = os.getenv("SIMPLENOTE_PASSWORD", "")
STARTED: bool = False
RELOAD_CALLS: int = -1
NOTE_FETCH_LENGTH: int = 1
_NOTE_CACHE_FILE: str = "_note_cache.pkl"
NOTE_CACHE_FILE: str = "note_cache.pkl"
os.remove(_NOTE_CACHE_FILE) if os.path.exists(_NOTE_CACHE_FILE) else None
os.remove(NOTE_CACHE_FILE) if os.path.exists(NOTE_CACHE_FILE) else None
DEFAULT_NOTE_TITLE: str = "untitled"
SIMPLENOTE_TOKEN_FILE: str = os.getenv("SIMPLENOTE_TOKEN_FILE", "simplenote_token.pkl")
SIMPLENOTE_STARTED: bool = False
SIMPLENOTE_RELOAD_CALLS: int = -1
SIMPLENOTE_NOTE_FETCH_LENGTH: int = 1
SIMPLENOTE_NOTE_CACHE_FILE: str = "note_cache.pkl"
SIMPLENOTE_DEFAULT_NOTE_TITLE: str = "untitled"


class Development(_BaseConfig):
Expand All @@ -87,6 +84,13 @@ class Production(_BaseConfig):
CONFIG: typing.Type[_BaseConfig] = _BaseConfig.mapping.get(env, Development)


# for key, value in vars(CONFIG).items():
for attr in dir(CONFIG):
if attr.startswith("_"):
continue
print(attr, getattr(CONFIG, attr))
os.environ[attr] = str(getattr(CONFIG, attr))
print("os.environ", os.environ)
from importlib import import_module


Expand Down
44 changes: 20 additions & 24 deletions api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,29 @@
from urllib.parse import urlencode
import uuid

from _config import CONFIG
from utils.patterns.singleton.base import Singleton
from utils.request import request
from utils.tools import Settings


logger = logging.getLogger()

__all__ = ["Simplenote"]

SIMPLENOTE_DIR = os.environ.get("SIMPLENOTE_DIR", "")
SIMPLENOTE_APP_ID: str = os.environ.get("SIMPLENOTE_APP_ID", "")
SIMPLENOTE_APP_KEY: str = os.environ.get("SIMPLENOTE_APP_KEY", "")
SIMPLENOTE_BUCKET: str = os.environ.get("SIMPLENOTE_BUCKET", "")
_SIMPLENOTE_TOKEN_FILE = os.environ.get("SIMPLENOTE_TOKEN_FILE", "simplenote_token.pkl")
SIMPLENOTE_TOKEN_FILE = os.path.join(SIMPLENOTE_DIR, _SIMPLENOTE_TOKEN_FILE)
simplenote_variables = [SIMPLENOTE_DIR, SIMPLENOTE_APP_ID, SIMPLENOTE_APP_KEY, SIMPLENOTE_BUCKET, SIMPLENOTE_TOKEN_FILE]
if not all(simplenote_variables):
raise Exception("Simplenote variables %s must be set in environment variables" % simplenote_variables)


class URL:
BASE: str = "https://api.simperium.com/1"
DATA = f"{BASE}/{CONFIG.SIMPLENOTE_APP_ID}/{CONFIG.SIMPLENOTE_BUCKET}"
__auth = f"{BASE}/{CONFIG.SIMPLENOTE_APP_ID}/authorize/"
DATA = f"{BASE}/{SIMPLENOTE_APP_ID}/{SIMPLENOTE_BUCKET}"
__auth = f"{BASE}/{SIMPLENOTE_APP_ID}/authorize/"
__index = DATA + "/index"
__retrieve = DATA + "/i/%s"
__modify = __retrieve
Expand Down Expand Up @@ -79,27 +89,11 @@ class SimplenoteLoginFailed(Exception):
class Simplenote(Singleton):
"""Class for interacting with the simplenote web service"""

# TODO: This should be a relative path, not an absolute one
TOKEN_FILE = "/Users/nut/Library/Application Support/Sublime Text/Packages/Simplenote/pkl/simplenote.pkl"

if not os.path.exists("pkl"):
os.makedirs("pkl")
assert os.path.exists("pkl"), "pkl directory does not exist!"
assert os.path.isdir("pkl"), "pkl is not a directory!"
assert os.access("pkl", os.W_OK), "pkl directory is not writable!"
assert os.access("pkl", os.R_OK), "pkl directory is not readable!"
assert os.path.exists(TOKEN_FILE), "pkl/simplenote.pkl does not exist!"

def __init__(self, username: str = "", password: str = ""):
"""object constructor"""
super().__init__()
self.username = username
self.password = password
if not all([self.username, self.password]):
SETTINGS = Settings(os.path.join(CONFIG.BASE_DIR, CONFIG.SETTINGS_FILE))
self.username = SETTINGS.get("username", "")
self.password = SETTINGS.get("password", "")
logger.info(("username:", self.username, "password:", self.password))
assert all(map(bool, [self.username, self.password])), "username and password must be set"
self.header = "X-Simperium-Token"
self.mark = "mark"
Expand All @@ -116,7 +110,7 @@ def authenticate(cls, username: str, password: str):
Returns:
Simplenote API token as string
"""
headers = {"X-Simperium-API-Key": CONFIG.SIMPLENOTE_APP_KEY}
headers = {"X-Simperium-API-Key": SIMPLENOTE_APP_KEY}
request_data = {"username": username, "password": password}
logger.info(("request_data:", request_data, "headers:", headers))
response = request(URL.auth(), method="POST", headers=headers, data=request_data, data_as_json=False)
Expand All @@ -135,7 +129,7 @@ def authenticate(cls, username: str, password: str):
raise err
assert isinstance(token, str), "token is not a string: %s" % token
assert len(token) == 32, "token length is not 32: %s" % token
with open(cls.TOKEN_FILE, "wb") as fh:
with open(SIMPLENOTE_TOKEN_FILE, "wb") as fh:
pickle.dump(token, fh)
return token

Expand All @@ -151,7 +145,7 @@ def token(self):
"""
if not self._token:
try:
with open(self.TOKEN_FILE, "rb") as fh:
with open(SIMPLENOTE_TOKEN_FILE, "rb") as fh:
token = pickle.load(fh, encoding="utf-8")
self._token = token
except FileNotFoundError as err:
Expand All @@ -163,7 +157,7 @@ def token(self):

def index(
self,
limit: int = CONFIG.NOTE_FETCH_LENGTH,
limit: int = 1000,
data: bool = False,
):
"""Method to get the note list
Expand Down Expand Up @@ -333,6 +327,8 @@ def trash(self, note_id: str, version: Optional[int] = None):


if __name__ == "__main__":
from _config import CONFIG

simplenote = Simplenote(username=CONFIG.SIMPLENOTE_USERNAME, password=CONFIG.SIMPLENOTE_PASSWORD)
token = simplenote.token
print("token: %s" % token)
4 changes: 1 addition & 3 deletions models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from typing import Dict, List, Optional, TypedDict
import uuid

from _config import CONFIG
from api import Simplenote


Expand Down Expand Up @@ -74,15 +73,14 @@ def __eq__(self, value: "Note") -> bool:
return self.id == value.id and self.v == value.v

@staticmethod
def index(limit: int = CONFIG.NOTE_FETCH_LENGTH, data: bool = True) -> List["Note"]:
def index(limit: int = 1000, data: bool = True) -> List["Note"]:
status, result = API.index(limit, data)
assert status == 0
assert isinstance(result, dict)
assert "index" in result
_notes = result.get("index", [])
assert status == 0, "Error retrieving notes"
assert isinstance(_notes, list)
logger.warning(_notes)
return [Note(**note) for note in _notes]

@staticmethod
Expand Down
92 changes: 26 additions & 66 deletions simplenote.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import time
from typing import Any, Dict, List, Optional

from _config import CONFIG
from api import Simplenote
from models import Note

Expand All @@ -21,20 +20,25 @@
logger = logging.getLogger()


PACKAGE_PATH = os.path.join(sublime.packages_path(), CONFIG.PROJECT_NAME)
TEMP_PATH = os.path.join(PACKAGE_PATH, "temp")
# SETTINGS = sublime.load_settings(CONFIG.SETTINGS_FILE)
SETTINGS: Settings = Settings(os.path.join(PACKAGE_PATH, CONFIG.SETTINGS_FILE))
API = Simplenote()
SIMPLENOTE_PROJECT_NAME = os.environ.get("SIMPLENOTE_PROJECT_NAME", "Simplenote")
SIMPLENOTE_PACKAGE_PATH = os.path.join(sublime.packages_path(), SIMPLENOTE_PROJECT_NAME)
SIMPLENOTE_DEFAULT_NOTE_TITLE = os.environ.get("SIMPLENOTE_DEFAULT_NOTE_TITLE", "Untitled")
SIMPLENOTE_TEMP_PATH = os.path.join(SIMPLENOTE_PACKAGE_PATH, "temp")
_SIMPLENOTE_NOTE_CACHE_FILE = os.environ.get("SIMPLENOTE_NOTE_CACHE_FILE", "note_cache.pkl")
SIMPLENOTE_NOTE_CACHE_FILE = os.path.join(SIMPLENOTE_PACKAGE_PATH, _SIMPLENOTE_NOTE_CACHE_FILE)
SIMPLENOTE_NOTE_FETCH_LENGTH = int(os.environ.get("SIMPLENOTE_NOTE_FETCH_LENGTH", 1000))
_SIMPLENOTE_SETTINGS_FILE = os.environ.get("SIMPLENOTE_SETTINGS_FILE", "simplenote.sublime-settings")
SIMPLENOTE_SETTINGS_FILE = os.path.join(SIMPLENOTE_PACKAGE_PATH, _SIMPLENOTE_SETTINGS_FILE)
# SETTINGS = sublime.load_settings(SIMPLENOTE_SETTINGS_FILE)
SETTINGS: Settings = Settings(SIMPLENOTE_SETTINGS_FILE)
API = Simplenote(SETTINGS.get("username", ""), SETTINGS.get("password", ""))


class _BaseManager(Singleton):
pass


class Local(_BaseManager):
_NOTE_CACHE_FILE_PATH = os.path.join(PACKAGE_PATH, CONFIG._NOTE_CACHE_FILE)
NOTE_CACHE_FILE_PATH = os.path.join(PACKAGE_PATH, CONFIG.NOTE_CACHE_FILE)

def __init__(self):
super().__init__()
Expand All @@ -61,60 +65,24 @@ def objects(self, value: List[Note]):
# @classmethod
def load_notes(self):
try:
with open(self._NOTE_CACHE_FILE_PATH, "rb") as cache_file:
self.notes = pickle.load(cache_file, encoding="utf-8")
except (EOFError, IOError, FileNotFoundError) as err:
logger.exception(err)
with open(self._NOTE_CACHE_FILE_PATH, "w+b") as cache_file:
pickle.dump(self._notes, cache_file)
logger.info((f"Created new note cache file: {self._NOTE_CACHE_FILE_PATH}"))
# logger.info(("Loaded notes length: ", len(cls.notes)))
try:
with open(self.NOTE_CACHE_FILE_PATH, "rb") as cache_file:
with open(SIMPLENOTE_NOTE_CACHE_FILE, "rb") as cache_file:
self.objects = pickle.load(cache_file, encoding="utf-8")
except (EOFError, IOError, FileNotFoundError) as err:
logger.exception(err)
with open(self.NOTE_CACHE_FILE_PATH, "w+b") as cache_file:
with open(SIMPLENOTE_NOTE_CACHE_FILE, "w+b") as cache_file:
pickle.dump(self._objects, cache_file)
logger.info((f"Created new objects cache file: {self.NOTE_CACHE_FILE_PATH}"))
logger.info((f"Created new objects cache file: {SIMPLENOTE_NOTE_CACHE_FILE}"))
# logger.info(("Loaded objects length: ", len(cls.objects)))

@staticmethod
def _save_notes(_NOTE_CACHE_FILE_PATH: str, notes: List[Dict[str, Any]]):
with open(_NOTE_CACHE_FILE_PATH, "w+b") as cache_file:
pickle.dump(notes, cache_file)

# @classmethod
def save_notes(cls):
cls._save_notes(cls._NOTE_CACHE_FILE_PATH, cls._notes)
# objects: List[Note] = []
# for note in cls.notes:
# d = {}
# for key in Note.__annotations__["d"].__annotations__.keys():
# if key in note:
# d[key] = note[key]
# kwargs = {
# "id": note["key"],
# "v": note["version"],
# "d": note,
# }
# objects.append(Note(**kwargs))
# cls.objects = objects
# cls._save_objects(cls.NOTE_CACHE_FILE_PATH, cls.objects)

@staticmethod
def _save_objects(NOTE_CACHE_FILE_PATH: str, objects: List[Note]):
with open(NOTE_CACHE_FILE_PATH, "w+b") as cache_file:
def _save_objects(SIMPLENOTE_NOTE_CACHE_FILE: str, objects: List[Note]):
with open(SIMPLENOTE_NOTE_CACHE_FILE, "w+b") as cache_file:
pickle.dump(objects, cache_file)

# @classmethod
def save_objects(cls):
return
cls._save_objects(cls.NOTE_CACHE_FILE_PATH, cls._objects)

# @staticmethod
# def dicts_to_models(notes: List[Dict[str, Any]]) -> List[Note]:
# return [Local.dict_to_model(note) for note in notes]
cls._save_objects(SIMPLENOTE_NOTE_CACHE_FILE, cls._objects)

@staticmethod
def dict_to_model(note: Dict[str, Any]) -> Note:
Expand All @@ -133,10 +101,6 @@ def dict_to_model(note: Dict[str, Any]) -> Note:
# def model_to_dict(note: Note) -> Dict[str, Any]:
# return note.d.__dict__

@classmethod
def save(cls):
cls.save_notes()

@staticmethod
def get_filename_for_note(title_extension_map: List[Dict], note: Note) -> str:
# Take out invalid characters from title and use that as base for the name
Expand Down Expand Up @@ -165,13 +129,13 @@ def _get_filename_for_note(cls, note: Note) -> str:

# @classmethod
def get_path_for_note(cls, note: Note):
return os.path.join(TEMP_PATH, cls._get_filename_for_note(note))
return os.path.join(SIMPLENOTE_TEMP_PATH, cls._get_filename_for_note(note))

# @classmethod
def get_note_from_path(cls, view_filepath: str):
note = None
if view_filepath:
if os.path.dirname(view_filepath) == TEMP_PATH:
if os.path.dirname(view_filepath) == SIMPLENOTE_TEMP_PATH:
view_note_filename = os.path.split(view_filepath)[1]
list__note_filename = [
note for note in cls._objects if cls._get_filename_for_note(note) == view_note_filename
Expand All @@ -192,20 +156,16 @@ def get_note_from_path(cls, view_filepath: str):


class Remote(_BaseManager):
def __init__(self):
# self._api: Simplenote = Simplenote(SETTINGS.get("username", ""), SETTINGS.get("password", ""))
# self._api: Simplenote = Simplenote(CONFIG.SIMPLENOTE_USERNAME, CONFIG.SIMPLENOTE_PASSWORD)
self._api: Optional[Simplenote] = None
_api: Simplenote

@functools.cached_property
def api(self) -> Simplenote:
# logger.info(("instance", self._api))
if not isinstance(self._api, Simplenote):
SETTINGS = sublime.load_settings(CONFIG.SETTINGS_FILE)
if not isinstance(getattr(self, "_api"), Simplenote):
# SETTINGS = sublime.load_settings(SIMPLENOTE_SETTINGS_FILE)
SETTINGS = Settings(SIMPLENOTE_SETTINGS_FILE)
self._api: Simplenote = Simplenote(
username=SETTINGS.get("username", ""), password=SETTINGS.get("password", "")
)
# self._instance = Simplenote(CONFIG.SIMPLENOTE_USERNAME, CONFIG.SIMPLENOTE_PASSWORD)
assert isinstance(self._api, Simplenote), f"Invalid Simplenote instance: {self._api}"
return self._api

Expand Down Expand Up @@ -311,15 +271,15 @@ def get_note_name(note: Note):
try:
content = note.d.content
except Exception as err:
return CONFIG.DEFAULT_NOTE_TITLE
return SIMPLENOTE_DEFAULT_NOTE_TITLE
index = content.find("\n")
if index > -1:
title = content[:index]
else:
if content:
title = content
else:
title = CONFIG.DEFAULT_NOTE_TITLE
title = SIMPLENOTE_DEFAULT_NOTE_TITLE
return title


Expand Down
Loading

0 comments on commit 21da7b6

Please sign in to comment.