import os import inspect from bleach import sanitizer from django.conf import settings as d_settings __all__ = ("Settings", "settings") # code from django-fox class BaseSettings: """Utility class used to load and save settings, can be used as model. Some members are excluded from being configuration: - Protected/private members; - On django model, "objects" and "Meta"; - Class declaration and callables Example: ``` class MySettings(Settings): a = 13 b = 12 my_settings = MySettings().load('MY_SETTINGS_KEY') print(my_settings.a, my_settings.get('b')) ``` This will load values from django project settings. """ def __init__(self, key, module=None): self.load(key, module) def load(self, key, module=None): """Load settings from module's item specified by its member name. When no module is provided, uses ``django.conf.settings``. :param str key: module member name. :param module: configuration object. :returns self """ if module is None: module = d_settings settings = getattr(module, key, None) if settings: self.update(settings) return self def update(self, settings): """Update self's values from provided settings. ``settings`` can be an iterable of ``(key, value)``. :param dict|Settings|iterable settings: value to update from. """ if isinstance(settings, (dict, Settings)): settings = settings.items() for key, value in settings: if self.is_config_item(key, value): setattr(self, key, value) def get(self, key, default=None): """Return settings' value for provided key.""" return getattr(self, key, default) def items(self): """Iterate over items members, as tupple of ``key, value``.""" for key in dir(self): value = getattr(self, key) if self.is_config_item(key, value): yield key, value def is_config_item(self, key, value): """Return True if key/value item is a configuration setting.""" if key.startswith("_") or callable(value) or inspect.isclass(value): return False return True class Settings(BaseSettings): # --- Global & misc DEFAULT_USER_GROUPS = { "radio hosts": ( # TODO include content_type in order to avoid clash with potential # extra applications # aircox "view_program", "view_episode", "change_diffusion", "add_comment", "change_comment", "delete_comment", "add_article", "change_article", "delete_article", "change_sound", "add_track", "change_track", "delete_track", # taggit "add_tag", "change_tag", "delete_tag", # filer "add_folder", "change_folder", "delete_folder", "can_use_directory_listing", "add_image", "change_image", "delete_image", ), } """Groups to assign to users at their creation, along with the permissions to add to each group.""" PROGRAMS_DIR = "programs" """Directory for the programs data.""" @property def PROGRAMS_DIR_ABS(self): return os.path.join(d_settings.MEDIA_ROOT, self.PROGRAMS_DIR) # --- Programs & episodes EPISODE_TITLE = "{program.title} - {date}" """Default title for episodes.""" EPISODE_TITLE_DATE_FORMAT = "%-d %B %Y" """Date format in episode title (python's strftime)""" # --- Logs & archives LOGS_ARCHIVES_DIR = "logs/archives" """Directory where to save logs' archives.""" @property def LOGS_ARCHIVES_DIR_ABS(self): return os.path.join(d_settings.PROJECT_ROOT, self.LOGS_ARCHIVES_DIR) LOGS_ARCHIVES_AGE = 60 """In days, minimal age of a log before it is archived.""" # --- Sounds SOUND_BROADCASTS_SUBDIR = "archives" """Sub directory used for the complete episode sounds.""" SOUND_EXCERPTS_SUBDIR = "excerpts" """Sub directory used for the excerpts of the episode.""" SOUND_QUALITY = { "attribute": "RMS lev dB", "range": (-18.0, -8.0), "sample_length": 120, } """Quality attributes passed to sound_quality_check from sounds_monitor (Soxi parameters).""" SOUND_FILE_EXT = (".ogg", ".flac", ".wav", ".mp3", ".opus") """Extension of sound files.""" SOUND_KEEP_DELETED = False """Tag sounds as deleted instead of deleting them when file has been removed from filesystem (sound monitoring).""" # --- Streamer & Controllers CONTROLLERS_WORKING_DIR = "/tmp/aircox" """Controllers working directory.""" # --- Playlist import from CSV IMPORT_PLAYLIST_CSV_COLS = ( "artist", "title", "minutes", "seconds", "tags", "info", ) """Columns for CSV file.""" IMPORT_PLAYLIST_CSV_DELIMITER = ";" """Column delimiter of csv text files.""" IMPORT_PLAYLIST_CSV_TEXT_QUOTE = '"' """Text delimiter of csv text files.""" ALLOW_COMMENTS = True """Allow comments.""" # ---- bleach ALLOWED_TAGS = [*sanitizer.ALLOWED_TAGS, "br", "p", "hr", "h2", "h3", "h4", "h5", "iframe", "pre"] ALLOWED_ATTRIBUTES = [*sanitizer.ALLOWED_ATTRIBUTES, "src", "width", "height", "frameborder", "href"] ALLOWED_PROTOCOLS = sanitizer.ALLOWED_PROTOCOLS settings = Settings("AIRCOX")