Commit bebb389e authored by Martin Matějek's avatar Martin Matějek

WIP; very simple serialization

parent 70eb6a61
#!/usr/bin/env python3
import argparse
import os
from notifylib.api import Api
......@@ -28,27 +29,56 @@ def create_argparser():
parser_action = subparsers.add_parser("add", help="Add new notification")
parser_action.add_argument("message", help="Notification message")
parser_action.add_argument("-t", "--template", help="Notification type / template", default='simple')
parser_action.add_argument("--persistent", help="Persistent notification (default: false)", action="store_true")
parser_list = subparsers.add_parser("list", help="List notification")
parser_list.add_argument("target", help="List multiple things o your choice", choices=["all", "plugins", "templates"], nargs="?", default="all")
# parser_list.add_argument("--id", help="ID of notification")
parser_list.add_argument("plugins", help="List available plugins")
return parser
def process_args(parser, api, args):
def print_plugins(plugins):
"""Pretty print plugin list"""
print("Available plugins:")
for k, v in plugins.items():
def print_templates(templates):
"""Pretty print templates list"""
print("Available templates:")
for k, v in templates.items():
print("{} - {}".format(k, v))
def print_notifications(notifications):
"""Pretty print stored notifications"""
print("Stored notifications")
def process_args(parser, args):
"""Call module interface based on args"""
# not working yet
if args.config:
api = Api(os.path.abspath(args.config))
api = Api()
if args.command == 'add':
if args.command == 'list':
if args.plugins:
elif args.command == 'list':
if == 'all':
ret = api.get_notifications()
elif == 'plugins':
ret = api.get_plugins()
print("Available plugins:")
for k, v in ret.items():
elif == 'templates':
ret = api.get_templates()
......@@ -57,9 +87,7 @@ def main():
parser = create_argparser()
args = parser.parse_args()
api = Api()
process_args(parser, api, args)
process_args(parser, args)
if __name__ == '__main__':
import logging
from .config import config
from .pluginstorage import PluginStorage
from .notificationstorage import NotificationStorage
......@@ -14,16 +16,16 @@ class Api:
self.plugins = PluginStorage(config.get('settings', 'plugin_dir'))
self.notifications = NotificationStorage(
config.get('settings', 'persistent_dir'),
config.get('settings', 'volatile_dir'),
config.get('settings', 'persistent_dir'),
print("Debug: Available notification_types: {}".format(self.notifications.notification_types))
# self.logger.debug("Available notification_types: %s" % self.notifications.notification_types)
def init_logger(self):
"""Init new logger instance"""
self.logger = logging.getLogger("notifylib")
def get_plugins(self):
return self.plugins.get_all()
......@@ -40,15 +42,24 @@ class Api:
if msgid:
return self.notifications.get_notification(msgid)
def get_templates(self):
"""Return notification types from plugins"""
return self.notifications.get_notification_types()
# data manipulation
def create(self, **user_opts):
"""Create new notification"""
# get pre-filled skeleton of class Notification
notif = self.notifications.get_skeleton(user_opts['template'])
self.logger.debug("Create new notification; user opts entered: %s" % user_opts)
notif = self.notifications.get_new_instance(**user_opts)
print("Newly created notification: {}".format(notif))
self.logger.debug("Stored notifications: %s" % self.notifications.get_all())
# TODO: refactor
def call_action(self, mgsid, name, **kwargs):
"""Call action on notification"""
import configparser
def load_config(filename):
class Config:
def __init__(self):
def default_config(self):
self.conf = configparser.ConfigParser()
self.conf.set("settings", "volatile_dir", "/tmp")
self.conf.set("settings", "persistent_dir", "/srv")
self.conf.set("settings", "plugin_dir", "plugins")
self.conf.set("settings", "logfile", "notifylib.log")
def default_config():
conf = configparser.ConfigParser()
conf.set("settings", "volatile_dir", "/tmp")
conf.set("settings", "persistent_dir", "/srv")
conf.set("settings", "plugin_dir", "plugins")
conf.set("settings", "logfile", "notifylib.log")
def load_config(self, filename):
print("Loading config file '{}'".format(filename))
return conf
def get(self, section, key):
return self.conf.get(section, key)
config = default_config()
config = Config()
......@@ -2,15 +2,15 @@ import json
class Notification:
def __init__(self, name, template, actions, timestamp=None, **opts): = name
self.template = template
self.actions = actions
def __init__(self, timestamp, skeleton, persistent=False, **opts):
self.notif_id = timestamp
self.timestamp = timestamp
self.skeleton = skeleton
self.opts = opts
self.persistent = persistent
# TODO: parse opts into metadata
self.content = opts['message']
def from_file(cls, f):
......@@ -22,13 +22,22 @@ class Notification:
# TODO: log failure
def create_instance(cls, **args):
return cls(args)
def valid(self, timestamp):
"""If notification is still valid"""
def __str__(self):
def render(self):
"""Return rendered template as text"""
def serialize_metadata(self):
"""Return serialized data as json"""
return "Content:{}".format(self.content)
def __str__(self):
out = "{\n"
out += "\tbase_type: {}\n".format(self.skeleton)
out += "\ttimestamp: {}\n".format(self.timestamp)
out += "}\n"
return out
import logging
import os
from datetime import datetime as dt
from .notification import Notification
class NotificationStorage:
"""In-memory notification storage that serialize and deserialize them"""
def __init__(self, volatile_dir, persistent_dir, notification_types):
......@@ -6,30 +13,47 @@ class NotificationStorage:
self.storage_dirs = {
'persistent': persistent_dir,
'volatile': volatile_dir,
'fallback': None, # TBD
self.notification_types = notification_types # notification data types/templates
self.notifications = []
# self.cached = {}
def init_logger(self):
self.logger = logging.getLogger("notifylib")
def store(self, n):
"""Store in memory and serializate to disk"""
#def store_persistent(self, n):
# self.serialize(n, self.storage_dirs['persistent'])
def serialize(self, n):
if n.persistent:
storage_dir = self.storage_dirs['persistent']
storage_dir = self.storage_dirs['volatile']
# fallback_render_dir = storage_dirs['render_fallback']
# do something to render content
# fallback_content = n.render()
metadata_content = n.serialize_metadata()
#def store_volatile(self, n):
# self.serialize(n, self.storage_dirs['volatile'])
fileid = self.generate_id()
#def serialize(self, n, destination):
# """Serialize notification to disk"""
# # save metadata to FS
# # render fallback form
# pass
# save to disk
regular_file = os.path.join(storage_dir, "{}.json".format(fileid))
# fallback_file = os.path.join(fallback_render_dir, fileid)
# TODO: try/catch
with open(regular_file, 'w') as f:
# with open(fallback_file, 'w') as f:
# f.write(fallback_content)
def load(self, storage_dir):
"""Deserialize from FS"""
......@@ -40,9 +64,13 @@ class NotificationStorage:
# delete_from_fs()
# TODO: find better key to identify notification instance in dict
def get_skeleton(self, name):
"""Return notification instance with filled in mandatory attributes"""
return self.notification_types[name].create_instance()
def get_new_instance(self, **opts):
"""Return complete new notification instance based on notification type"""
self.logger.debug("Trying to create from template %s" % opts['template'])
timestamp = dt.utcnow()
return Notification(timestamp, self.notification_types[opts['template']], **opts)
# TODO: find better key to identify notification instance in dict
def get_notification(self, name):
......@@ -53,15 +81,22 @@ class NotificationStorage:
return self.cached[name]
def get_all(self):
return self.notifications
def get_notification_types(self):
"""Return all notification types"""
return self.notification_types
# TODO: WIP helper fce
def render_one(self, notif):
# # TODO: WIP helper fce
# def render_one(self, notif):
# pass
# def render_all(self):
# """Render all notifications"""
# for n in self.notifications:
# self.render_one(n)
def render_all(self):
"""Render all notifications"""
for n in self.notifications:
def generate_id(self):
"""Unique id of message based on timestamp"""
return dt.utcnow().timestamp()
import yaml
import logging
class Plugin:
......@@ -6,7 +7,9 @@ class Plugin: = name
self.actions = []
self.templates = []
self.notifications = []
self.notification_types = {}
for a in actions:
......@@ -14,8 +17,13 @@ class Plugin:
for t in templates:
self.logger.debug("%s" % notifications)
for n in notifications:
self.logger.debug("concrete notif: %s" % n)
self.notification_types[n['name']] = n
def init_logger(self):
self.logger = logging.getLogger("notifylib")
def from_file(cls, filename):
......@@ -32,12 +40,7 @@ class Plugin:
return self.actions
def get_notification_types(self):
nt = []
for n in self.notifications:
return nt
return self.notification_types
def __str__(self):
"""For debug purposes"""
......@@ -49,8 +52,8 @@ class Plugin:
for t in self.templates:
out += "\tTemplates: {}\n".format(t)
for n in self.notifications:
out += "\tNotifications: {}\n".format(n)
for name, data in self.notification_types.items():
out += "\tNotifications: {} : {}\n".format(name, data)
out += "}"
import os
import logging
from .plugin import Plugin
from .notificationskeleton import NotificationSkeleton
class PluginStorage:
......@@ -14,7 +16,7 @@ class PluginStorage:
def init_logger(self):
self.logger = logging.getLogger("notifylib")
def plugin_file_path(self, f):
return os.path.join(self.plugin_dir, f)
......@@ -29,9 +31,6 @@ class PluginStorage:
p = Plugin.from_file(self.plugin_file_path(f))
self.plugins[] = p
# don't walk into lower levels
def get_plugin(self, name):
return self.plugins[name]
......@@ -42,7 +41,15 @@ class PluginStorage:
def get_notification_types(self):
ret = {}
for k, v in self.plugins.items():
ret[k] = v.get_notification_types()
for name, plugin in self.plugins.items():
self.logger.debug("%s - %s" % (name, plugin))
args = plugin.get_notification_types()
self.logger.debug("Plugin metadata: %s" % args)
for n_name, n_data in args.items():
self.logger.debug("Notif data: %s" % n_data)
ret[n_name] = NotificationSkeleton(**n_data)
return ret
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment