X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/197fc570be54152c90c424379a00b36e28e0cf79..61c3753c6e0c567ccbb11115c787da22218ab5f6:/apps/compress/utils.py diff --git a/apps/compress/utils.py b/apps/compress/utils.py new file mode 100644 index 00000000..5e13222f --- /dev/null +++ b/apps/compress/utils.py @@ -0,0 +1,139 @@ +import os +import re +import tempfile + +from django.conf import settings as django_settings +from django.utils.http import urlquote +from django.dispatch import dispatcher + +from compress.conf import settings +from compress.signals import css_filtered, js_filtered + +def get_class(class_string): + """ + Convert a string version of a function name to the callable object. + """ + + if not hasattr(class_string, '__bases__'): + + try: + class_string = class_string.encode('ascii') + mod_name, class_name = get_mod_func(class_string) + if class_name != '': + class_string = getattr(__import__(mod_name, {}, {}, ['']), class_name) + except (ImportError, AttributeError): + raise Exception('Failed to import filter %s' % class_string) + + return class_string + +def get_mod_func(callback): + """ + Converts 'django.views.news.stories.story_detail' to + ('django.views.news.stories', 'story_detail') + """ + + try: + dot = callback.rindex('.') + except ValueError: + return callback, '' + return callback[:dot], callback[dot+1:] + +def needs_update(output_file, source_files, verbosity=0): + """ + Scan the source files for changes and returns True if the output_file needs to be updated. + """ + + version = get_version(source_files) + + on = get_output_filename(output_file, version) + compressed_file_full = media_root(on) + + if not os.path.exists(compressed_file_full): + return True, version + + update_needed = getattr(get_class(settings.COMPRESS_VERSIONING)(), 'needs_update')(output_file, source_files, version) + return update_needed + +def media_root(filename): + """ + Return the full path to ``filename``. ``filename`` is a relative path name in MEDIA_ROOT + """ + return os.path.join(django_settings.STATIC_ROOT, filename) + +def media_url(url, prefix=None): + if prefix: + return prefix + urlquote(url) + return django_settings.STATIC_URL + urlquote(url) + +def concat(filenames, separator=''): + """ + Concatenate the files from the list of the ``filenames``, ouput separated with ``separator``. + """ + r = '' + for filename in filenames: + fd = open(media_root(filename), 'rb') + r += fd.read() + r += separator + fd.close() + return r + +def max_mtime(files): + return int(max([os.stat(media_root(f)).st_mtime for f in files])) + +def save_file(filename, contents): + dirname = os.path.dirname(media_root(filename)) + if not os.path.exists(dirname): + os.makedirs(dirname) + fd = open(media_root(filename), 'wb+') + fd.write(contents) + fd.close() + +def get_output_filename(filename, version): + if settings.COMPRESS_VERSION and version is not None: + return filename.replace(settings.COMPRESS_VERSION_PLACEHOLDER, version) + else: + return filename.replace(settings.COMPRESS_VERSION_PLACEHOLDER, settings.COMPRESS_VERSION_DEFAULT) + +def get_version(source_files, verbosity=0): + version = getattr(get_class(settings.COMPRESS_VERSIONING)(), 'get_version')(source_files) + return version + +def get_version_from_file(path, filename): + regex = re.compile(r'^%s$' % (get_output_filename(settings.COMPRESS_VERSION_PLACEHOLDER.join([re.escape(part) for part in filename.split(settings.COMPRESS_VERSION_PLACEHOLDER)]), r'([A-Za-z0-9]+)'))) + for f in os.listdir(path): + result = regex.match(f) + if result and result.groups(): + return result.groups()[0] + +def remove_files(path, filename, verbosity=0): + regex = re.compile(r'^%s$' % (os.path.basename(get_output_filename(settings.COMPRESS_VERSION_PLACEHOLDER.join([re.escape(part) for part in filename.split(settings.COMPRESS_VERSION_PLACEHOLDER)]), r'[A-Za-z0-9]+')))) + if os.path.exists(path): + for f in os.listdir(path): + if regex.match(f): + if verbosity >= 1: + print "Removing outdated file %s" % f + + os.unlink(os.path.join(path, f)) + +def filter_common(obj, verbosity, filters, attr, separator, signal): + output = concat(obj['source_filenames'], separator) + + filename = get_output_filename(obj['output_filename'], get_version(obj['source_filenames'])) + + if settings.COMPRESS_VERSION: + remove_files(os.path.dirname(media_root(filename)), obj['output_filename'], verbosity) + + if verbosity >= 1: + print "Saving %s" % filename + + for f in filters: + output = getattr(get_class(f)(verbose=(verbosity >= 2)), attr)(output) + + save_file(filename, output) + signal.send(None) + +def filter_css(css, verbosity=0): + return filter_common(css, verbosity, filters=settings.COMPRESS_CSS_FILTERS, attr='filter_css', separator='', signal=css_filtered) + +def filter_js(js, verbosity=0): + return filter_common(js, verbosity, filters=settings.COMPRESS_JS_FILTERS, attr='filter_js', separator='', signal=js_filtered)