Commit 84b69a87 by Piotr Mitros

Merge; removed bad code

parents 804147c0 510f0286
......@@ -7,3 +7,4 @@ def dump_to_db(mongodb, events):
## TODO: Error handling
collection = mongodb['event_log']
collection.insert([e.event for e in events])
......@@ -15,5 +15,5 @@ simplejson
South==0.7.6
django-celery==3.0.11
celery-with-redis==3.0
-e git://github.com/MITx/django-pipeline.git@c5a4848d3d8fa90a7da4a4007f5653be40cccdd9#egg=django_pipeline-dev
-e git://github.com/MITx/django-staticfiles.git@6d2504e5c84a3003b4573e0ba0f11adf7583d372#egg=django_staticfiles-dev
-e git://github.com/edx/django-pipeline.git@c5a4848d3d8fa90a7da4a4007f5653be40cccdd9#egg=django_pipeline-dev
-e git://github.com/edx/django-staticfiles.git@6d2504e5c84a3003b4573e0ba0f11adf7583d372#egg=django_staticfiles-dev
''' Decorators for analytics modules.
''' Decorators for analytics modules.
@view defines a user-visible view
@query defines a machine-readable SOA
......@@ -51,11 +51,11 @@ def event_handler(batch=True, per_user=False, per_resource=False,
def view(category = None, name = None, description = None, args = None):
''' This decorator is appended to a view in an analytics module. A
view will return HTML which will be shown to the user.
view will return HTML which will be shown to the user.
category: Optional specification for type (global, per-user,
etc.). If not given, this will be extrapolated from the
argspec. This should typically be omitted.
argspec. This should typically be omitted.
name: Optional specification for name shown to the user. This will
default to function name. In most cases, this is recommended.
......@@ -64,7 +64,7 @@ def view(category = None, name = None, description = None, args = None):
to the docstring.
args: Optional argspec for the function. This is generally better
omitted.
omitted.
'''
def view_factory(f):
registry.register_handler('view',category, name, description, f, args)
......@@ -74,20 +74,20 @@ def view(category = None, name = None, description = None, args = None):
def query(category = None, name = None, description = None, args = None):
''' This decorator is appended to a query in an analytics
module. A module will return output that can be used
programmatically (typically JSON).
programmatically (typically JSON).
category: Optional specification for type (global, per-user,
etc.). If not given, this will be extrapolated from the
argspec. This should typically be omitted.
argspec. This should typically be omitted.
name: Optional specification for name exposed via SOA. This will
default to function name. In most cases, this is recommended.
default to function name. In most cases, this is recommended.
description: Optional description exposed via the SOA
discovery. If not given, this will default to the docstring.
discovery. If not given, this will default to the docstring.
args: Optional argspec for the function. This is generally better
omitted.
omitted.
'''
def query_factory(f):
registry.register_handler('query',category, name, description, f, args)
......@@ -98,7 +98,7 @@ def query(category = None, name = None, description = None, args = None):
def memoize_query(cache_time = 60*4, timeout = 60*15, ignores = ["<class 'pymongo.database.Database'>", "<class 'fs.osfs.OSFS'>"]):
''' Call function only if we do not have the results for its execution already
We ignore parameters of type pymongo.database.Database and fs.osfs.OSFS. These
will be different per call, but function identically.
will be different per call, but function identically.
'''
def isuseful(a, ignores):
if str(type(a)) in ignores:
......@@ -156,8 +156,8 @@ def memoize_query(cache_time = 60*4, timeout = 60*15, ignores = ["<class 'pymong
def cron(period, params=None):
''' Run command periodically
Unknown whether or how well this works.
Unknown whether or how well this works.
'''
def factory(f):
@periodic_task(run_every=period, name=f.__name__)
......
### HACK HACK HACK ###
#
# This is a horrible hack to make a render() function for modules.
#
#
# This is a horrible hack to make a render() function for modules.
#
# In the future, each module should run within its own space
# (globals(), locals()), but for now, this kind of works.
# (globals(), locals()), but for now, this kind of works.
#
# This code:
# This code:
# 1. Looks at the stack to figure out the calling module
# 2. Matches that up to a module in INSTALLED_ANALYTICS_MODULE (based
# on longest matching path)
......@@ -13,12 +13,12 @@
# stored (this is part of setuptools)
# 4. Renders the template with Mako
#
# I apologize about this code, but the alternative would be to
# I apologize about this code, but the alternative would be to
# ship without this, in which case we'd accrue technical debt
# with each new module written.
#
# with each new module written.
#
### HACK HACK HACK ###
#
#
# This file also has a static file finder for the modules. This is a
# bit less of a hack (although the implementation is still somewhat
# crude), but it sure doesn't belong in core.render.
......@@ -36,6 +36,7 @@ from pkg_resources import resource_filename
from mako.lookup import TemplateLookup
from django.conf import settings
## Code borrowed from mitx/common/lib/tempdir
def mkdtemp_clean(suffix="", prefix="tmp", dir=None):
"""Just like mkdtemp, but the directory will be deleted when the process ends."""
......@@ -43,6 +44,7 @@ def mkdtemp_clean(suffix="", prefix="tmp", dir=None):
atexit.register(cleanup_tempdir, the_dir)
return the_dir
def cleanup_tempdir(the_dir):
"""Called on process exit to remove a temp directory."""
if os.path.exists(the_dir):
......@@ -53,30 +55,41 @@ if module_directory is None:
module_directory = mkdtemp_clean()
lookups = {}
def lookup(directory):
if directory in lookups:
return lookups[directory]
else:
l = TemplateLookup(directories = [directory],
module_directory = module_directory,
else:
l = TemplateLookup(directories=[directory],
module_directory=module_directory,
output_encoding='utf-8',
input_encoding='utf-8',
encoding_errors='replace')
lookups[directory] = l
return l
def render(templatefile, context, caller = None):
def render(templatefile, context, caller=None):
''' Render a template within a module.
1) Figure out the module this is being called from.
2) Use resource_filename from packagetools to figure out that module's templates directory
3) Use the mako template engine to render it.
Hacks:
1) We are not caching anything. This will cause performance issues for deployment.
2) The way we inspect the stack is quite messy. We should do this better.
'''
stack = traceback.extract_stack()
if not caller:
if not caller:
caller_path = os.path.abspath(stack[-2][0])
# For testing, use: sys.modules.keys() if sys.modules[module] and '__file__' in sys.modules[module].__dict__]#
analytics_modules = [sys.modules[module] for module in settings.INSTALLED_ANALYTICS_MODULES]
analytics_modules.sort(key = lambda x : len(os.path.commonprefix([x.__file__, os.path.abspath(caller_path)])))
# For testing, use: sys.modules.keys() if sys.modules[module] and '__file__' in sys.modules[module].__dict__]#
analytics_modules = [sys.modules[module] for module in settings.INSTALLED_ANALYTICS_MODULES]
analytics_modules.sort(key=lambda x: len(os.path.commonprefix([os.path.abspath(x.__file__), os.path.abspath(caller_path)])))
caller_module = analytics_modules[-1]
caller_name = caller_module.__name__
template_directory = os.path.abspath(resource_filename(caller_name, "templates"))
template = lookup(template_directory).get_template(templatefile)
return template.render_unicode(**context)
......@@ -86,11 +99,12 @@ from django.contrib.staticfiles.finders import BaseFinder
from django.contrib.staticfiles import utils
from django.core.files.storage import FileSystemStorage
class ModuleStorage(FileSystemStorage):
''' Storage which allows static files to live with prefixes in the
static directory which do not exist in the filesystem. A file can
exist as /modules/foo/static/bar.html in the source filesystem,
but as /djmodules/foo/bar.html in the static_files directory.
but as /djmodules/foo/bar.html in the static_files directory.
'''
def path(self, name):
''' Returns the absolute path to a file, stripping out
......@@ -109,9 +123,10 @@ class ModuleStorage(FileSystemStorage):
return ["djmodules"], []
elif path in ["djmodules", "djmodules/", "/djmodules", "/djmodules/"]:
return [self.base_url.split('/')[1]], []
else:
else:
return FileSystemStorage.listdir(self, path)
class ModuleFileFinder(BaseFinder):
''' Finds the static files for all installed analytics
modules. Does magic to put them in the right place in the URL
......@@ -122,7 +137,7 @@ class ModuleFileFinder(BaseFinder):
self.load_static()
def load_static(self):
''' Walk all modules. Find paths. Create Django storages for them (Django's poorman's pyfs).
''' Walk all modules. Find paths. Create Django storages for them (Django's poorman's pyfs).
'''
self.module_paths = [(module.split('.')[-1], os.path.abspath(resource_filename(module, "static"))) for module in settings.INSTALLED_ANALYTICS_MODULES]
self.static_paths = [(module, path, ModuleStorage(path, os.path.join("djmodules", module))) for module, path in self.module_paths]
......@@ -142,5 +157,3 @@ class ModuleFileFinder(BaseFinder):
for module, path, storage in self.static_paths:
for path in utils.get_files(storage, ignore_patterns):
yield path, storage
......@@ -149,19 +149,13 @@ def djt_event_property_check(cache, events):
if "event_property_check" in evt:
cache.set("last_seen_user", evt.djt_agent, 30)
@query(name='djt_fake_user_count')
def djt_fake_user_count_query():
@query()
def djt_fake_user_count():
return 2
@view(name='djt_fake_user_count')
def djt_fake_user_count_view(query):
@view()
def djt_fake_user_count(query):
''' Test of an abstraction used to call queries, abstracting away
the network, as well as optional parameters like fs, mongodb, etc.
the network, as well as optional parameters like fs, db, etc.
'''
return "<html>Users: {uc}</html>".format(uc = query.djt_fake_user_count())
@event_handler()
def router(events):
for e in events:
if e.university == "Harvard":
track_event(e)
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