"""
persistence.py:
contains what we need to make a (category) objects (see models.py) persist
beyond application scope and load them from outside sources
For now we can save, load and list
* to/from a file (using a turtle rdf graph serialization for categories)
* to/from a github repository (using a turtle rdf graph serialization
for categories)
"""
from abc import ABCMeta, abstractmethod
from catedit import app, github
from base64 import b64encode, b64decode
from flask.ext.github import GitHubError
import os
import json
LOGGER = app.logger
class Persistence:
"""
Meta-class for all persistence classes
"""
__metaclass__ = ABCMeta
@abstractmethod
def save(self, **kwargs):
"""
Abstract - Saves object
"""
return
@abstractmethod
def delete(self, **kwargs):
"""
Abstract - Deletes object
"""
return
@abstractmethod
def load(self, **kwargs):
"""
Abstract - Loads object
"""
return
@abstractmethod
def list(self, **kwargs):
"""
Abstract - Lists objects
"""
return
class PersistenceToFile(Persistence):
"""
Persistence Class for saving to a local file (see config.py
for tweaking)
Expected kwargs for saving to/loading from a file are:
* name : name of the file to write in/read from
* content : desired content of the file when writing
"""
def save(self, **kwargs):
"""
Saves to a file
"""
path_to_save = app.config["FILE_SAVE_DIRECTORY"]+kwargs["name"]
file_to_save = open(path_to_save, 'wb')
file_to_save.write(kwargs["content"])
file_to_save.close()
def load(self, **kwargs):
"""
Loads from a file
"""
path_to_load = app.config["FILE_SAVE_DIRECTORY"]+kwargs["name"]
file_to_load = open(path_to_load, 'rb')
file_content = file_to_load.read()
file_to_load.close()
return file_content
def delete(self, **kwargs):
"""
Deletes a file
"""
path_to_delete = app.config["FILE_SAVE_DIRECTORY"]+kwargs["name"]
os.remove(path_to_delete)
# IDEA: return { file_name: file_content } type dict
def list(self, **kwargs):
"""
Lists all files in file directory (as set in config.py)
"""
file_content_list = []
for file_name in os.listdir(app.config["FILE_SAVE_DIRECTORY"]):
if not file_name or file_name[0] == ".":
continue
path_to_load = open(app.config["FILE_SAVE_DIRECTORY"]+file_name)
file_content = path_to_load.read()
path_to_load.close()
file_content_list.append(file_content)
# LOGGER.debug(file_content_list)
return file_content_list
class PersistenceToGithub(Persistence):
"""
Persistence Class for saving to/loading from a Github repository (see
config.py for tweaks)
Expected kwargs for saving to/loading from a Github repository are:
* name : name of the file to write in/read from
* content : desired content of the file when writing
* message : when saving or deleting, commit message to be saved
to Github
"""
def save(self, **kwargs):
"""
Saves to a Github repository
IMPORTANT: To save to a file Github, we use a PUT request, and
if the file already exists we must put its sha in the request data.
The first try-except block actually expects an error if creating a
new file because we need to check if the file already exists
"""
# LOGGER.debug(kwargs["content"])
request_data = {"content": str(b64encode(kwargs["content"]), "ascii"),
"message": kwargs["message"]}
try:
filedict = github.get("repos/"
+ app.config["REPOSITORY_OWNER"]+"/"
+ app.config["REPOSITORY_NAME"]
+ "/contents/"
+ app.config["CATEGORIES_PATH"]
+ kwargs["name"])
request_data["sha"] = filedict["sha"]
except GitHubError as ghe:
LOGGER.debug("Github sent an error, either: \
1- You're trying to create a new file named : "
+ kwargs["name"] + " OR \
2- You're trying to edit a file named : "
+ kwargs["name"] + ", \
in which case something went wrong \
trying to get its sha")
LOGGER.debug(ghe.response.json())
# LOGGER.debug(json.dumps(request_data))
try:
github.request('PUT',
"repos/"
+ app.config["REPOSITORY_OWNER"]+"/"
+ app.config["REPOSITORY_NAME"]
+ "/contents/"
+ app.config["CATEGORIES_PATH"]
+ kwargs["name"],
data=json.dumps(request_data))
except GitHubError as ghe:
LOGGER.debug("Github Error trying to update file: "+kwargs["name"])
LOGGER.debug("Github sent an error, if 404, either you may not \
have access to the repository or it doesn't exist ")
LOGGER.debug(ghe.response.json())
def load(self, **kwargs):
"""
Loads from a Github repository
"""
try:
filedict = github.get("repos/"
+ app.config["REPOSITORY_OWNER"]+"/"
+ app.config["REPOSITORY_NAME"]
+ "/contents/"
+ app.config["CATEGORIES_PATH"]
+ kwargs["name"])
file_content = str(b64decode(filedict["content"]), "utf-8")
except GitHubError as ghe:
LOGGER.debug("Github Error trying to get file: "+kwargs["name"])
LOGGER.debug("Github sent an error, if 404, either you may not \
have access to the repository or it doesn't exist ")
LOGGER.debug(ghe.response.text)
return file_content
def delete(self, **kwargs):
"""
Deletes from a Github repository
"""
request_data = {"message": kwargs["message"]}
try:
filedict = github.get("repos/"
+ app.config["REPOSITORY_OWNER"]+"/"
+ app.config["REPOSITORY_NAME"]
+ "/contents/"
+ app.config["CATEGORIES_PATH"]
+ kwargs["name"])
request_data["sha"] = filedict["sha"]
except GitHubError as ghe:
LOGGER.debug("Github Error trying to get sha for \
file: "+kwargs["name"])
LOGGER.debug(ghe.response.text)
try:
github.request('DELETE',
"repos/catedit-system/"
+ app.config["REPOSITORY_NAME"]
+ "/contents/categories/"
+ kwargs["name"],
data=json.dumps(request_data))
except GitHubError as ghe:
LOGGER.debug("Github Error trying to delete file: "+kwargs["name"])
LOGGER.debug(ghe.response.text)
def list(self, **kwargs):
"""
Lists all files in the github repository (as set in config.py)
"""
filenames_list = []
try:
files_in_repo = github.get("repos/"
+ app.config["REPOSITORY_OWNER"]+"/"
+ app.config["REPOSITORY_NAME"]
+ "/contents/"
+ app.config["CATEGORIES_PATH"])
filenames_list = [github_file["name"]
for github_file in files_in_repo]
# LOGGER.debug(filenames_list)
except GitHubError as ghe:
LOGGER.debug("Github Error trying to get the file list in the \
category repository")
LOGGER.debug("NOTE: Github sent an error, if 404 either you \
may not have access to the repository or it doesn't \
exist or there isn't any files in it")
LOGGER.debug(ghe.response.text)
file_content_list = []
for filename in filenames_list:
try:
filedict = github.get("repos/"
+ app.config["REPOSITORY_OWNER"]+"/"
+ app.config["REPOSITORY_NAME"]
+ "/contents/"
+ app.config["CATEGORIES_PATH"]
+ filename)
file_content_list.append(str(b64decode(filedict["content"]),
"utf-8"))
except GitHubError as ghe:
LOGGER.debug("Github Error trying to get file: "+filename)
LOGGER.debug(ghe.response.text)
LOGGER.debug(file_content_list)
return file_content_list