| 1 | """
|
|---|
| 2 | django_extensions.management.jobs
|
|---|
| 3 | """
|
|---|
| 4 |
|
|---|
| 5 | import os
|
|---|
| 6 | from imp import find_module
|
|---|
| 7 |
|
|---|
| 8 | _jobs = None
|
|---|
| 9 |
|
|---|
| 10 | def noneimplementation(meth):
|
|---|
| 11 | return None
|
|---|
| 12 |
|
|---|
| 13 | class JobError(Exception):
|
|---|
| 14 | pass
|
|---|
| 15 |
|
|---|
| 16 | class BaseJob(object):
|
|---|
| 17 | help = "undefined job description."
|
|---|
| 18 | when = None
|
|---|
| 19 |
|
|---|
| 20 | def execute(self):
|
|---|
| 21 | raise NotImplementedError("Job needs to implement the execute method")
|
|---|
| 22 |
|
|---|
| 23 | class HourlyJob(BaseJob):
|
|---|
| 24 | when = "hourly"
|
|---|
| 25 |
|
|---|
| 26 | class DailyJob(BaseJob):
|
|---|
| 27 | when = "daily"
|
|---|
| 28 |
|
|---|
| 29 | class WeeklyJob(BaseJob):
|
|---|
| 30 | when = "weekly"
|
|---|
| 31 |
|
|---|
| 32 | class MonthlyJob(BaseJob):
|
|---|
| 33 | when = "monthly"
|
|---|
| 34 |
|
|---|
| 35 | def my_import(name):
|
|---|
| 36 | imp = __import__(name)
|
|---|
| 37 | mods = name.split('.')
|
|---|
| 38 | if len(mods)>1:
|
|---|
| 39 | for mod in mods[1:]:
|
|---|
| 40 | imp = getattr(imp, mod)
|
|---|
| 41 | return imp
|
|---|
| 42 |
|
|---|
| 43 | def find_jobs(jobs_dir):
|
|---|
| 44 | try:
|
|---|
| 45 | return [f[:-3] for f in os.listdir(jobs_dir) \
|
|---|
| 46 | if not f.startswith('_') and f.endswith(".py")]
|
|---|
| 47 | except OSError:
|
|---|
| 48 | return []
|
|---|
| 49 |
|
|---|
| 50 | def find_job_module(app_name, when=None):
|
|---|
| 51 | parts = app_name.split('.')
|
|---|
| 52 | parts.append('jobs')
|
|---|
| 53 | if when:
|
|---|
| 54 | parts.append(when)
|
|---|
| 55 | parts.reverse()
|
|---|
| 56 | path = None
|
|---|
| 57 | while parts:
|
|---|
| 58 | part = parts.pop()
|
|---|
| 59 | f, path, descr = find_module(part, path and [path] or None)
|
|---|
| 60 | return path
|
|---|
| 61 |
|
|---|
| 62 | def import_job(app_name, name, when=None):
|
|---|
| 63 | jobmodule = "%s.jobs.%s%s" % (app_name, when and "%s." % when or "", name)
|
|---|
| 64 | job_mod = my_import(jobmodule)
|
|---|
| 65 | # todo: more friendly message for AttributeError if job_mod does not exist
|
|---|
| 66 | try:
|
|---|
| 67 | job = job_mod.Job
|
|---|
| 68 | except:
|
|---|
| 69 | raise JobError("Job module %s does not contain class instance named 'Job'" % jobmodule)
|
|---|
| 70 | if when and not (job.when == when or job.when == None):
|
|---|
| 71 | raise JobError("Job %s is not a %s job." % (jobmodule, when))
|
|---|
| 72 | return job
|
|---|
| 73 |
|
|---|
| 74 | def get_jobs(when=None, only_scheduled=False):
|
|---|
| 75 | """
|
|---|
| 76 | Returns a dictionary mapping of job names together with there respective
|
|---|
| 77 | application class.
|
|---|
| 78 | """
|
|---|
| 79 | global _jobs
|
|---|
| 80 | # FIXME: HACK: make sure the project dir is on the path when executed as ./manage.py
|
|---|
| 81 | import sys
|
|---|
| 82 | try:
|
|---|
| 83 | cpath = os.path.dirname(os.path.realpath(sys.argv[0]))
|
|---|
| 84 | ppath = os.path.dirname(cpath)
|
|---|
| 85 | if ppath not in sys.path:
|
|---|
| 86 | sys.path.append(ppath)
|
|---|
| 87 | except:
|
|---|
| 88 | pass
|
|---|
| 89 | if _jobs is None:
|
|---|
| 90 | _jobs = {}
|
|---|
| 91 | if True:
|
|---|
| 92 | from django.conf import settings
|
|---|
| 93 | for app_name in settings.INSTALLED_APPS:
|
|---|
| 94 | scandirs = (None, 'hourly', 'daily', 'weekly', 'monthly')
|
|---|
| 95 | if when:
|
|---|
| 96 | scandirs = None, when
|
|---|
| 97 | for subdir in scandirs:
|
|---|
| 98 | try:
|
|---|
| 99 | path = find_job_module(app_name, subdir)
|
|---|
| 100 | for name in find_jobs(path):
|
|---|
| 101 | if (app_name, name) in _jobs:
|
|---|
| 102 | raise JobError("Duplicate job %s" % name)
|
|---|
| 103 | job = import_job(app_name, name, subdir)
|
|---|
| 104 | if only_scheduled and job.when == None:
|
|---|
| 105 | # only include jobs which are scheduled
|
|---|
| 106 | continue
|
|---|
| 107 | if when and job.when != when:
|
|---|
| 108 | # generic job not in same schedule
|
|---|
| 109 | continue
|
|---|
| 110 | _jobs[(app_name, name)] = job
|
|---|
| 111 | except ImportError:
|
|---|
| 112 | pass # No job module -- continue scanning
|
|---|
| 113 | return _jobs
|
|---|
| 114 |
|
|---|
| 115 | def get_job(app_name, job_name):
|
|---|
| 116 | jobs = get_jobs()
|
|---|
| 117 | if app_name:
|
|---|
| 118 | return jobs[(app_name, job_name)]
|
|---|
| 119 | else:
|
|---|
| 120 | for a, j in jobs.keys():
|
|---|
| 121 | if j==job_name:
|
|---|
| 122 | return jobs[(a, j)]
|
|---|
| 123 | raise KeyError("Job not found: %s" % job_name)
|
|---|
| 124 |
|
|---|
| 125 | def print_jobs(when=None, only_scheduled=False, show_when=True, \
|
|---|
| 126 | show_appname=False, show_header=True):
|
|---|
| 127 | jobmap = get_jobs(when, only_scheduled=only_scheduled)
|
|---|
| 128 | print "Job List: %i jobs" % len(jobmap)
|
|---|
| 129 | jlist = jobmap.keys()
|
|---|
| 130 | jlist.sort()
|
|---|
| 131 | appname_spacer = "%%-%is" % max(len(e[0]) for e in jlist)
|
|---|
| 132 | name_spacer = "%%-%is" % max(len(e[1]) for e in jlist)
|
|---|
| 133 | when_spacer = "%%-%is" % max(len(e.when) for e in jobmap.values() if e.when)
|
|---|
| 134 | if show_header:
|
|---|
| 135 | line = " "
|
|---|
| 136 | if show_appname:
|
|---|
| 137 | line += appname_spacer % "appname" + " - "
|
|---|
| 138 | line += name_spacer % "jobname"
|
|---|
| 139 | if show_when:
|
|---|
| 140 | line += " - " + when_spacer % "when"
|
|---|
| 141 | line += " - help"
|
|---|
| 142 | print line
|
|---|
| 143 | print "-"*80
|
|---|
| 144 |
|
|---|
| 145 | for app_name, job_name in jlist:
|
|---|
| 146 | job = jobmap[(app_name, job_name)]
|
|---|
| 147 | line = " "
|
|---|
| 148 | if show_appname:
|
|---|
| 149 | line += appname_spacer % app_name + " - "
|
|---|
| 150 | line += name_spacer % job_name
|
|---|
| 151 | if show_when:
|
|---|
| 152 | line += " - " + when_spacer % (job.when and job.when or "")
|
|---|
| 153 | line += " - " + job.help
|
|---|
| 154 | print line
|
|---|