mirror of
https://github.com/nikdoof/test-auth.git
synced 2025-12-14 06:42:16 +00:00
Removed django_cron dependancy, use manual started cronjobs
This commit is contained in:
4
cronjobs.txt
Normal file
4
cronjobs.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
@daily ./run-cron.py reddit.cron UpdateAPIs
|
||||||
|
@daily ./run-cron.py eve_api.cron UpdateAPIs
|
||||||
|
@hourly ./run-cron.py sso.cron RemoveInvalidUsers
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 34
|
|
||||||
/svn/!svn/ver/32/trunk/django_cron
|
|
||||||
END
|
|
||||||
base.py
|
|
||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 42
|
|
||||||
/svn/!svn/ver/32/trunk/django_cron/base.py
|
|
||||||
END
|
|
||||||
__init__.py
|
|
||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 46
|
|
||||||
/svn/!svn/ver/16/trunk/django_cron/__init__.py
|
|
||||||
END
|
|
||||||
signals.py
|
|
||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 44
|
|
||||||
/svn/!svn/ver/8/trunk/django_cron/signals.py
|
|
||||||
END
|
|
||||||
models.py
|
|
||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 44
|
|
||||||
/svn/!svn/ver/16/trunk/django_cron/models.py
|
|
||||||
END
|
|
||||||
README.txt
|
|
||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 45
|
|
||||||
/svn/!svn/ver/16/trunk/django_cron/README.txt
|
|
||||||
END
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
9
|
|
||||||
|
|
||||||
dir
|
|
||||||
32
|
|
||||||
http://django-cron.googlecode.com/svn/trunk/django_cron
|
|
||||||
http://django-cron.googlecode.com/svn
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2009-10-28T14:21:55.477932Z
|
|
||||||
32
|
|
||||||
jim.mixtake@gmail.com
|
|
||||||
|
|
||||||
|
|
||||||
svn:special svn:externals svn:needs-lock
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
857153d3-df44-0410-aaef-ab1f86cdcd94
|
|
||||||
|
|
||||||
base.py
|
|
||||||
file
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2010-02-20T20:20:49.000000Z
|
|
||||||
afab29654c6380b9c2ecd00fae92522c
|
|
||||||
2009-10-28T14:21:55.477932Z
|
|
||||||
32
|
|
||||||
jim.mixtake@gmail.com
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
4064
|
|
||||||
|
|
||||||
__init__.py
|
|
||||||
file
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2010-02-20T20:20:49.000000Z
|
|
||||||
8492cf50017e7eff784fdfbe0cc8b3ba
|
|
||||||
2009-06-02T13:59:46.682354Z
|
|
||||||
16
|
|
||||||
Jim.mixtake
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2566
|
|
||||||
|
|
||||||
signals.py
|
|
||||||
file
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2010-02-20T20:20:49.000000Z
|
|
||||||
8e3eba63f720bd6d830fc89d7d6be693
|
|
||||||
2008-10-26T12:02:48.705921Z
|
|
||||||
5
|
|
||||||
digitalxero
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1192
|
|
||||||
|
|
||||||
models.py
|
|
||||||
file
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2010-02-20T20:20:49.000000Z
|
|
||||||
329f05f78f78465d4acfba95c42d32f7
|
|
||||||
2009-06-02T13:59:46.682354Z
|
|
||||||
16
|
|
||||||
Jim.mixtake
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1632
|
|
||||||
|
|
||||||
README.txt
|
|
||||||
file
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2010-02-20T20:20:49.000000Z
|
|
||||||
801836776103697e20d5d9a9446cb28b
|
|
||||||
2009-06-02T13:59:46.682354Z
|
|
||||||
16
|
|
||||||
Jim.mixtake
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1953
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
9
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
= How to install djang-cron =
|
|
||||||
|
|
||||||
1. Put 'django_cron' into your python path
|
|
||||||
|
|
||||||
2. Add 'django_cron' to INSTALLED_APPS in your settings.py file
|
|
||||||
|
|
||||||
3. Add the following code to the beginning of your urls.py file (just after the imports):
|
|
||||||
|
|
||||||
import django_cron
|
|
||||||
django_cron.autodiscover()
|
|
||||||
|
|
||||||
|
|
||||||
4. Create a file called 'cron.py' inside each installed app that you want to add a recurring job to. The app must be installed via the INSTALLED_APPS in your settings.py or the autodiscover will not find it.
|
|
||||||
|
|
||||||
=== Important note ===
|
|
||||||
|
|
||||||
If you are using mod_python, you need to make sure your server is set up to server more than one request per instance, Otherwise it will kill django-cron before the tasks get started. The specific line to look for is in your 'httpd.conf' file:
|
|
||||||
|
|
||||||
|
|
||||||
# THIS IS BAD!!! IT WILL CRIPPLE DJANGO-CRON
|
|
||||||
MaxRequestsPerChild 1
|
|
||||||
|
|
||||||
|
|
||||||
Change it to a value that is large enough that your cron jobs will get run at least once per instance. We're working on resolving this issue without dictating your server config.
|
|
||||||
|
|
||||||
In the meantime, django_cron is best used to execute tasks that occur relatively often (at least once an hour). Try setting MaxRequestsPerChild to 50, 100, or 200
|
|
||||||
|
|
||||||
# Depending on traffic, and your server config, a number between 50 and 500 is probably good
|
|
||||||
# Note: the higher this number, the more memory django is likely to use. Be careful on shared hosting
|
|
||||||
MaxRequestsPerChild 100
|
|
||||||
|
|
||||||
|
|
||||||
== Example cron.py ==
|
|
||||||
|
|
||||||
from django_cron import cronScheduler, Job
|
|
||||||
|
|
||||||
# This is a function I wrote to check a feedback email address
|
|
||||||
# and add it to our database. Replace with your own imports
|
|
||||||
from MyMailFunctions import check_feedback_mailbox
|
|
||||||
|
|
||||||
class CheckMail(Job):
|
|
||||||
"""
|
|
||||||
Cron Job that checks the lgr users mailbox and adds any
|
|
||||||
approved senders' attachments to the db
|
|
||||||
"""
|
|
||||||
|
|
||||||
# run every 300 seconds (5 minutes)
|
|
||||||
run_every = 300
|
|
||||||
|
|
||||||
def job(self):
|
|
||||||
# This will be executed every 5 minutes
|
|
||||||
check_feedback_mailbox()
|
|
||||||
|
|
||||||
cronScheduler.register(CheckMail)
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
from base import Job, cronScheduler
|
|
||||||
|
|
||||||
def autodiscover():
|
|
||||||
"""
|
|
||||||
Auto-discover INSTALLED_APPS cron.py modules and fail silently when
|
|
||||||
not present. This forces an import on them to register any cron jobs they
|
|
||||||
may want.
|
|
||||||
"""
|
|
||||||
import imp
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
for app in settings.INSTALLED_APPS:
|
|
||||||
# For each app, we need to look for an cron.py inside that app's
|
|
||||||
# package. We can't use os.path here -- recall that modules may be
|
|
||||||
# imported different ways (think zip files) -- so we need to get
|
|
||||||
# the app's __path__ and look for cron.py on that path.
|
|
||||||
|
|
||||||
# Step 1: find out the app's __path__ Import errors here will (and
|
|
||||||
# should) bubble up, but a missing __path__ (which is legal, but weird)
|
|
||||||
# fails silently -- apps that do weird things with __path__ might
|
|
||||||
# need to roll their own cron registration.
|
|
||||||
try:
|
|
||||||
app_path = __import__(app, {}, {}, [app.split('.')[-1]]).__path__
|
|
||||||
except AttributeError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Step 2: use imp.find_module to find the app's admin.py. For some
|
|
||||||
# reason imp.find_module raises ImportError if the app can't be found
|
|
||||||
# but doesn't actually try to import the module. So skip this app if
|
|
||||||
# its admin.py doesn't exist
|
|
||||||
try:
|
|
||||||
imp.find_module('cron', app_path)
|
|
||||||
except ImportError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Step 3: import the app's cron file. If this has errors we want them
|
|
||||||
# to bubble up.
|
|
||||||
__import__("%s.cron" % app)
|
|
||||||
|
|
||||||
# Step 4: once we find all the cron jobs, start the cronScheduler
|
|
||||||
cronScheduler.execute()
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
import cPickle
|
|
||||||
from threading import Timer
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from django.dispatch import dispatcher
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from signals import cron_done
|
|
||||||
import models
|
|
||||||
|
|
||||||
# how often to check if jobs are ready to be run (in seconds)
|
|
||||||
# in reality if you have a multithreaded server, it may get checked
|
|
||||||
# more often that this number suggests, so keep an eye on it...
|
|
||||||
# default value: 300 seconds == 5 min
|
|
||||||
polling_frequency = getattr(settings, "CRON_POLLING_FREQUENCY", 300)
|
|
||||||
|
|
||||||
class Job(object):
|
|
||||||
# 86400 seconds == 24 hours
|
|
||||||
run_every = 86400
|
|
||||||
|
|
||||||
def run(self, *args, **kwargs):
|
|
||||||
self.job()
|
|
||||||
cron_done.send(sender=self, *args, **kwargs)
|
|
||||||
|
|
||||||
def job(self):
|
|
||||||
"""
|
|
||||||
Should be overridden (this way is cleaner, but the old way - overriding run() - will still work)
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class CronScheduler(object):
|
|
||||||
def register(self, job_class, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Register the given Job with the scheduler class
|
|
||||||
"""
|
|
||||||
|
|
||||||
job_instance = job_class()
|
|
||||||
|
|
||||||
if not isinstance(job_instance, Job):
|
|
||||||
raise TypeError("You can only register a Job not a %r" % job_class)
|
|
||||||
|
|
||||||
job, created = models.Job.objects.get_or_create(name=str(job_instance.__class__))
|
|
||||||
if created:
|
|
||||||
job.instance = cPickle.dumps(job_instance)
|
|
||||||
job.args = cPickle.dumps(args)
|
|
||||||
job.kwargs = cPickle.dumps(kwargs)
|
|
||||||
job.run_frequency = job_instance.run_every
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
def execute(self):
|
|
||||||
"""
|
|
||||||
Queue all Jobs for execution
|
|
||||||
"""
|
|
||||||
status, created = models.Cron.objects.get_or_create(pk=1)
|
|
||||||
|
|
||||||
# This is important for 2 reasons:
|
|
||||||
# 1. It keeps us for running more than one instance of the
|
|
||||||
# same job at a time
|
|
||||||
# 2. It reduces the number of polling threads because they
|
|
||||||
# get killed off if they happen to check while another
|
|
||||||
# one is already executing a job (only occurs with
|
|
||||||
# multi-threaded servers)
|
|
||||||
if status.executing:
|
|
||||||
return
|
|
||||||
|
|
||||||
status.executing = True
|
|
||||||
try:
|
|
||||||
status.save()
|
|
||||||
except:
|
|
||||||
# this will fail if you're debugging, so we want it
|
|
||||||
# to fail silently and start the timer again so we
|
|
||||||
# can pick up where we left off once debugging is done
|
|
||||||
Timer(polling_frequency, self.execute).start()
|
|
||||||
return
|
|
||||||
|
|
||||||
jobs = models.Job.objects.all()
|
|
||||||
for job in jobs:
|
|
||||||
if job.queued:
|
|
||||||
time_delta = datetime.now() - job.last_run
|
|
||||||
if (time_delta.seconds + 86400*time_delta.days) > job.run_frequency:
|
|
||||||
inst = cPickle.loads(str(job.instance))
|
|
||||||
args = cPickle.loads(str(job.args))
|
|
||||||
kwargs = cPickle.loads(str(job.kwargs))
|
|
||||||
|
|
||||||
try:
|
|
||||||
inst.run(*args, **kwargs)
|
|
||||||
job.last_run = datetime.now()
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
# if the job throws an error, just remove it from
|
|
||||||
# the queue. That way we can find/fix the error and
|
|
||||||
# requeue the job manually
|
|
||||||
job.queued = False
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
status.executing = False
|
|
||||||
status.save()
|
|
||||||
|
|
||||||
# Set up for this function to run again
|
|
||||||
Timer(polling_frequency, self.execute).start()
|
|
||||||
|
|
||||||
|
|
||||||
cronScheduler = CronScheduler()
|
|
||||||
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
from django.db import models
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
class Job(models.Model):
|
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
|
|
||||||
# time between job runs (in seconds) // default: 1 day
|
|
||||||
run_frequency = models.PositiveIntegerField(default=86400)
|
|
||||||
last_run = models.DateTimeField(default=datetime.now())
|
|
||||||
|
|
||||||
instance = models.TextField()
|
|
||||||
args = models.TextField()
|
|
||||||
kwargs = models.TextField()
|
|
||||||
queued = models.BooleanField(default=True)
|
|
||||||
|
|
||||||
class Cron(models.Model):
|
|
||||||
executing = models.BooleanField(default=False)
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
from django.dispatch import Signal
|
|
||||||
|
|
||||||
cron_queued = Signal()
|
|
||||||
cron_done = Signal(providing_args=["job"])
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
= How to install djang-cron =
|
|
||||||
|
|
||||||
1. Put 'django_cron' into your python path
|
|
||||||
|
|
||||||
2. Add 'django_cron' to INSTALLED_APPS in your settings.py file
|
|
||||||
|
|
||||||
3. Add the following code to the beginning of your urls.py file (just after the imports):
|
|
||||||
|
|
||||||
import django_cron
|
|
||||||
django_cron.autodiscover()
|
|
||||||
|
|
||||||
|
|
||||||
4. Create a file called 'cron.py' inside each installed app that you want to add a recurring job to. The app must be installed via the INSTALLED_APPS in your settings.py or the autodiscover will not find it.
|
|
||||||
|
|
||||||
=== Important note ===
|
|
||||||
|
|
||||||
If you are using mod_python, you need to make sure your server is set up to server more than one request per instance, Otherwise it will kill django-cron before the tasks get started. The specific line to look for is in your 'httpd.conf' file:
|
|
||||||
|
|
||||||
|
|
||||||
# THIS IS BAD!!! IT WILL CRIPPLE DJANGO-CRON
|
|
||||||
MaxRequestsPerChild 1
|
|
||||||
|
|
||||||
|
|
||||||
Change it to a value that is large enough that your cron jobs will get run at least once per instance. We're working on resolving this issue without dictating your server config.
|
|
||||||
|
|
||||||
In the meantime, django_cron is best used to execute tasks that occur relatively often (at least once an hour). Try setting MaxRequestsPerChild to 50, 100, or 200
|
|
||||||
|
|
||||||
# Depending on traffic, and your server config, a number between 50 and 500 is probably good
|
|
||||||
# Note: the higher this number, the more memory django is likely to use. Be careful on shared hosting
|
|
||||||
MaxRequestsPerChild 100
|
|
||||||
|
|
||||||
|
|
||||||
== Example cron.py ==
|
|
||||||
|
|
||||||
from django_cron import cronScheduler, Job
|
|
||||||
|
|
||||||
# This is a function I wrote to check a feedback email address
|
|
||||||
# and add it to our database. Replace with your own imports
|
|
||||||
from MyMailFunctions import check_feedback_mailbox
|
|
||||||
|
|
||||||
class CheckMail(Job):
|
|
||||||
"""
|
|
||||||
Cron Job that checks the lgr users mailbox and adds any
|
|
||||||
approved senders' attachments to the db
|
|
||||||
"""
|
|
||||||
|
|
||||||
# run every 300 seconds (5 minutes)
|
|
||||||
run_every = 300
|
|
||||||
|
|
||||||
def job(self):
|
|
||||||
# This will be executed every 5 minutes
|
|
||||||
check_feedback_mailbox()
|
|
||||||
|
|
||||||
cronScheduler.register(CheckMail)
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
from base import Job, cronScheduler
|
|
||||||
|
|
||||||
def autodiscover():
|
|
||||||
"""
|
|
||||||
Auto-discover INSTALLED_APPS cron.py modules and fail silently when
|
|
||||||
not present. This forces an import on them to register any cron jobs they
|
|
||||||
may want.
|
|
||||||
"""
|
|
||||||
import imp
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
for app in settings.INSTALLED_APPS:
|
|
||||||
# For each app, we need to look for an cron.py inside that app's
|
|
||||||
# package. We can't use os.path here -- recall that modules may be
|
|
||||||
# imported different ways (think zip files) -- so we need to get
|
|
||||||
# the app's __path__ and look for cron.py on that path.
|
|
||||||
|
|
||||||
# Step 1: find out the app's __path__ Import errors here will (and
|
|
||||||
# should) bubble up, but a missing __path__ (which is legal, but weird)
|
|
||||||
# fails silently -- apps that do weird things with __path__ might
|
|
||||||
# need to roll their own cron registration.
|
|
||||||
try:
|
|
||||||
app_path = __import__(app, {}, {}, [app.split('.')[-1]]).__path__
|
|
||||||
except AttributeError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Step 2: use imp.find_module to find the app's admin.py. For some
|
|
||||||
# reason imp.find_module raises ImportError if the app can't be found
|
|
||||||
# but doesn't actually try to import the module. So skip this app if
|
|
||||||
# its admin.py doesn't exist
|
|
||||||
try:
|
|
||||||
imp.find_module('cron', app_path)
|
|
||||||
except ImportError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Step 3: import the app's cron file. If this has errors we want them
|
|
||||||
# to bubble up.
|
|
||||||
__import__("%s.cron" % app)
|
|
||||||
|
|
||||||
# Step 4: once we find all the cron jobs, start the cronScheduler
|
|
||||||
cronScheduler.execute()
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
import cPickle
|
|
||||||
from threading import Timer
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from django.dispatch import dispatcher
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from signals import cron_done
|
|
||||||
import models
|
|
||||||
|
|
||||||
# how often to check if jobs are ready to be run (in seconds)
|
|
||||||
# in reality if you have a multithreaded server, it may get checked
|
|
||||||
# more often that this number suggests, so keep an eye on it...
|
|
||||||
# default value: 300 seconds == 5 min
|
|
||||||
polling_frequency = getattr(settings, "CRON_POLLING_FREQUENCY", 300)
|
|
||||||
|
|
||||||
class Job(object):
|
|
||||||
# 86400 seconds == 24 hours
|
|
||||||
run_every = 86400
|
|
||||||
|
|
||||||
def run(self, *args, **kwargs):
|
|
||||||
self.job()
|
|
||||||
cron_done.send(sender=self, *args, **kwargs)
|
|
||||||
|
|
||||||
def job(self):
|
|
||||||
"""
|
|
||||||
Should be overridden (this way is cleaner, but the old way - overriding run() - will still work)
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class CronScheduler(object):
|
|
||||||
def register(self, job_class, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Register the given Job with the scheduler class
|
|
||||||
"""
|
|
||||||
|
|
||||||
job_instance = job_class()
|
|
||||||
|
|
||||||
if not isinstance(job_instance, Job):
|
|
||||||
raise TypeError("You can only register a Job not a %r" % job_class)
|
|
||||||
|
|
||||||
job, created = models.Job.objects.get_or_create(name=str(job_instance.__class__))
|
|
||||||
if created:
|
|
||||||
job.instance = cPickle.dumps(job_instance)
|
|
||||||
job.args = cPickle.dumps(args)
|
|
||||||
job.kwargs = cPickle.dumps(kwargs)
|
|
||||||
job.run_frequency = job_instance.run_every
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
def execute(self):
|
|
||||||
"""
|
|
||||||
Queue all Jobs for execution
|
|
||||||
"""
|
|
||||||
status, created = models.Cron.objects.get_or_create(pk=1)
|
|
||||||
|
|
||||||
# This is important for 2 reasons:
|
|
||||||
# 1. It keeps us for running more than one instance of the
|
|
||||||
# same job at a time
|
|
||||||
# 2. It reduces the number of polling threads because they
|
|
||||||
# get killed off if they happen to check while another
|
|
||||||
# one is already executing a job (only occurs with
|
|
||||||
# multi-threaded servers)
|
|
||||||
if status.executing:
|
|
||||||
return
|
|
||||||
|
|
||||||
status.executing = True
|
|
||||||
try:
|
|
||||||
status.save()
|
|
||||||
except:
|
|
||||||
# this will fail if you're debugging, so we want it
|
|
||||||
# to fail silently and start the timer again so we
|
|
||||||
# can pick up where we left off once debugging is done
|
|
||||||
Timer(polling_frequency, self.execute).start()
|
|
||||||
return
|
|
||||||
|
|
||||||
jobs = models.Job.objects.all()
|
|
||||||
for job in jobs:
|
|
||||||
if job.queued:
|
|
||||||
time_delta = datetime.now() - job.last_run
|
|
||||||
if (time_delta.seconds + 86400*time_delta.days) > job.run_frequency:
|
|
||||||
inst = cPickle.loads(str(job.instance))
|
|
||||||
args = cPickle.loads(str(job.args))
|
|
||||||
kwargs = cPickle.loads(str(job.kwargs))
|
|
||||||
|
|
||||||
try:
|
|
||||||
inst.run(*args, **kwargs)
|
|
||||||
job.last_run = datetime.now()
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
# if the job throws an error, just remove it from
|
|
||||||
# the queue. That way we can find/fix the error and
|
|
||||||
# requeue the job manually
|
|
||||||
job.queued = False
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
status.executing = False
|
|
||||||
status.save()
|
|
||||||
|
|
||||||
# Set up for this function to run again
|
|
||||||
Timer(polling_frequency, self.execute).start()
|
|
||||||
|
|
||||||
|
|
||||||
cronScheduler = CronScheduler()
|
|
||||||
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
from django.db import models
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
class Job(models.Model):
|
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
|
|
||||||
# time between job runs (in seconds) // default: 1 day
|
|
||||||
run_frequency = models.PositiveIntegerField(default=86400)
|
|
||||||
last_run = models.DateTimeField(default=datetime.now())
|
|
||||||
|
|
||||||
instance = models.TextField()
|
|
||||||
args = models.TextField()
|
|
||||||
kwargs = models.TextField()
|
|
||||||
queued = models.BooleanField(default=True)
|
|
||||||
|
|
||||||
class Cron(models.Model):
|
|
||||||
executing = models.BooleanField(default=False)
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
"""
|
|
||||||
from django.dispatch import Signal
|
|
||||||
|
|
||||||
cron_queued = Signal()
|
|
||||||
cron_done = Signal(providing_args=["job"])
|
|
||||||
@@ -22,12 +22,10 @@ def import_eve_account(api_key, user_id):
|
|||||||
"""
|
"""
|
||||||
Imports an account from the API into the EVEAccount model.
|
Imports an account from the API into the EVEAccount model.
|
||||||
"""
|
"""
|
||||||
print user_id, ":", api_key
|
|
||||||
auth_params = {'userID': user_id, 'apiKey': api_key}
|
auth_params = {'userID': user_id, 'apiKey': api_key}
|
||||||
account_doc = CachedDocument.objects.api_query('/account/Characters.xml.aspx',
|
account_doc = CachedDocument.objects.api_query('/account/Characters.xml.aspx',
|
||||||
params=auth_params,
|
params=auth_params,
|
||||||
no_cache=False)
|
no_cache=False)
|
||||||
#print account_doc.body
|
|
||||||
|
|
||||||
dom = minidom.parseString(account_doc.body)
|
dom = minidom.parseString(account_doc.body)
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django_cron import cronScheduler, Job
|
|
||||||
from eve_api.models.api_player import EVEAccount, EVEPlayerCorporation
|
from eve_api.models.api_player import EVEAccount, EVEPlayerCorporation
|
||||||
import eve_api.api_puller.accounts
|
import eve_api.api_puller.accounts
|
||||||
from eve_api.api_exceptions import APIAuthException, APINoUserIDException
|
from eve_api.api_exceptions import APIAuthException, APINoUserIDException
|
||||||
|
|
||||||
class UpdateAPIs(Job):
|
class UpdateAPIs():
|
||||||
"""
|
"""
|
||||||
Updates all Eve API elements in the database
|
Updates all Eve API elements in the database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# run every 2 hours
|
|
||||||
run_every = 7200
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _logger(self):
|
def _logger(self):
|
||||||
if not hasattr(self, '__logger'):
|
if not hasattr(self, '__logger'):
|
||||||
@@ -36,6 +32,3 @@ class UpdateAPIs(Job):
|
|||||||
|
|
||||||
for corp in EVEPlayerCorporation.objects.all():
|
for corp in EVEPlayerCorporation.objects.all():
|
||||||
corp.query_and_update_corp()
|
corp.query_and_update_corp()
|
||||||
|
|
||||||
|
|
||||||
cronScheduler.register(UpdateAPIs)
|
|
||||||
|
|||||||
@@ -192,9 +192,7 @@ class EVEPlayerCorporation(EVEAPIModel):
|
|||||||
continue
|
continue
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# Something weird has happened
|
# Something weird has happened
|
||||||
print " * Index Error:", tag_map[0]
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print "Updating", self.id, self.name
|
|
||||||
self.api_last_updated = datetime.utcnow()
|
self.api_last_updated = datetime.utcnow()
|
||||||
self.save()
|
self.save()
|
||||||
|
|||||||
@@ -1,18 +1,13 @@
|
|||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django_cron import cronScheduler, Job
|
|
||||||
from reddit.models import RedditAccount
|
from reddit.models import RedditAccount
|
||||||
from reddit.api import Inbox
|
from reddit.api import Inbox
|
||||||
|
|
||||||
class UpdateAPIs(Job):
|
class UpdateAPIs():
|
||||||
"""
|
"""
|
||||||
Updates all Reddit API elements in the database
|
Updates all Reddit API elements in the database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# run every 24 hours
|
|
||||||
run_every = 86400
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _logger(self):
|
def _logger(self):
|
||||||
if not hasattr(self, '__logger'):
|
if not hasattr(self, '__logger'):
|
||||||
@@ -60,5 +55,3 @@ class ProcessInbox(Job):
|
|||||||
else:
|
else:
|
||||||
print key.username
|
print key.username
|
||||||
|
|
||||||
|
|
||||||
cronScheduler.register(UpdateAPIs)
|
|
||||||
|
|||||||
29
run-cron.py
Executable file
29
run-cron.py
Executable file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Executes a Django cronjob"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from django.core.management import setup_environ
|
||||||
|
import settings
|
||||||
|
|
||||||
|
setup_environ(settings)
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
log = logging.getLogger('runcron')
|
||||||
|
|
||||||
|
try:
|
||||||
|
mod = __import__(sys.argv[1])
|
||||||
|
except ImportError:
|
||||||
|
raise Exception('Error creating service')
|
||||||
|
|
||||||
|
for i in sys.argv[1].split(".")[1:]:
|
||||||
|
mod = getattr(mod, i)
|
||||||
|
cron_class = getattr(mod, sys.argv[2])()
|
||||||
|
|
||||||
|
log.info("Starting Job %s in %s" % (sys.argv[2], sys.argv[1])
|
||||||
|
|
||||||
|
try:
|
||||||
|
cron_class.job()
|
||||||
|
except:
|
||||||
|
log.error("Error executing job, aborting.")
|
||||||
|
|
||||||
|
log.info("Job complete")
|
||||||
@@ -75,7 +75,6 @@ INSTALLED_APPS = (
|
|||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
'django_evolution',
|
'django_evolution',
|
||||||
'registration',
|
'registration',
|
||||||
'django_cron',
|
|
||||||
'eve_proxy',
|
'eve_proxy',
|
||||||
'eve_api',
|
'eve_api',
|
||||||
'mumble',
|
'mumble',
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django_cron import cronScheduler, Job
|
|
||||||
from django.contrib.auth.models import User, Group
|
from django.contrib.auth.models import User, Group
|
||||||
from eve_api.models import EVEAccount
|
from eve_api.models import EVEAccount
|
||||||
from sso.models import ServiceAccount
|
from sso.models import ServiceAccount
|
||||||
|
|
||||||
class RemoveInvalidUsers(Job):
|
class RemoveInvalidUsers():
|
||||||
"""
|
"""
|
||||||
Cycles through all users, check if their permissions are correct.
|
Cycles through all users, check if their permissions are correct.
|
||||||
"""
|
"""
|
||||||
@@ -27,7 +26,7 @@ class RemoveInvalidUsers(Job):
|
|||||||
# Check each service account and delete access if they're not allowed
|
# Check each service account and delete access if they're not allowed
|
||||||
for servacc in ServiceAccount.objects.filter(user=user):
|
for servacc in ServiceAccount.objects.filter(user=user):
|
||||||
if not (set(user.groups.all()) & set(servacc.service.groups.all())):
|
if not (set(user.groups.all()) & set(servacc.service.groups.all())):
|
||||||
print "User %s is not in allowed group for %s, deleting account" % (user.username, servacc.service)
|
self._logger.info("User %s is not in allowed group for %s, deleting account" % (user.username, servacc.service))
|
||||||
servacc.delete()
|
servacc.delete()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -39,5 +38,4 @@ class RemoveInvalidUsers(Job):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
cronScheduler.register(RemoveInvalidUsers)
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user