|
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 |