PageRenderTime 27ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/stopforumspam/management/commands/sfsupdate.py

https://github.com/benjaoming/django-stopforumspam
Python | 126 lines | 100 code | 19 blank | 7 comment | 17 complexity | 931d2c90007f56da1579d65070aa4e14 MD5 | raw file
  1. from __future__ import absolute_import, unicode_literals
  2. import re
  3. import zipfile
  4. from io import BytesIO
  5. from django.conf import settings
  6. from django.core.management.base import BaseCommand
  7. from django.db import transaction
  8. from django.utils import timezone
  9. from ... import settings as sfs_settings
  10. from ... import compat, models
  11. class Command(BaseCommand):
  12. help = 'Updates the database with the latest IPs from stopforumspam.com'
  13. def add_arguments(self, parser):
  14. parser.add_argument(
  15. '--force',
  16. action='store_true',
  17. dest='force',
  18. default=False,
  19. help='Force update of options',
  20. )
  21. parser.add_argument(
  22. '--quiet', '-q',
  23. action='store_true',
  24. dest='quiet',
  25. default=False,
  26. help='Do not produce output unless upon failure',
  27. )
  28. def handle(self, *args, **options):
  29. self.quiet = options['quiet']
  30. self.ensure_updated(force=options['force'])
  31. def print_output(self, msg):
  32. if not self.quiet:
  33. print(msg)
  34. def ensure_updated(self, force=False, quiet=False):
  35. last_update = models.Log.objects.filter(
  36. message=sfs_settings.LOG_MESSAGE_UPDATE).order_by('-inserted')
  37. do_update = force
  38. if not do_update and last_update.count() > 0:
  39. days_ago = timezone.now() - last_update[0].inserted
  40. if days_ago.days >= sfs_settings.CACHE_EXPIRE:
  41. do_update = True
  42. else:
  43. do_update = True
  44. if do_update:
  45. self.print_output("Updating (this may take some time)")
  46. self.print_output(
  47. "If you abort this command and want to rebuild, you have to use the --force option!")
  48. self.do_update()
  49. else:
  50. self.print_output("Nothing to update")
  51. @compat.notrans
  52. def do_update(self):
  53. try:
  54. self._do_update()
  55. transaction.commit()
  56. except Exception:
  57. transaction.rollback()
  58. raise
  59. def _do_update(self):
  60. # Delete old cache
  61. models.Cache.objects.filter(permanent=False).delete()
  62. # First log the update
  63. log = models.Log()
  64. log.message = sfs_settings.LOG_MESSAGE_UPDATE
  65. log.save()
  66. # For security purposes we test that each line is actually an IP
  67. # address
  68. ip_pattern = re.compile(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")
  69. fileobject = None
  70. if sfs_settings.SOURCE_ZIP.startswith("file://"):
  71. filename = sfs_settings.SOURCE_ZIP.split("file://")[1]
  72. self.print_output(" . Using: {}".format(filename))
  73. fileobject = open(filename, "rb")
  74. else:
  75. self.print_output(" ^ Downloading: {}".format(sfs_settings.SOURCE_ZIP))
  76. response = compat.urllib.urlopen(sfs_settings.SOURCE_ZIP)
  77. # Necessary because ZipFile needs to do a seek() call on the
  78. # file object which HttpResponse in python3 doesn't support.
  79. fileobject = BytesIO(response.read())
  80. z = zipfile.ZipFile(fileobject)
  81. self.print_output(" < Extracting: {}".format(sfs_settings.ZIP_FILENAME))
  82. ips = str(z.read(sfs_settings.ZIP_FILENAME))
  83. inserted = 0
  84. total = len(ips)
  85. objects_to_save = []
  86. for ip in ip_pattern.findall(ips):
  87. cache = models.Cache(ip=ip)
  88. objects_to_save.append(cache)
  89. inserted = inserted + 1
  90. if inserted % 100 == 0:
  91. self.print_output("Object %d of %d found" % (inserted, total))
  92. self.print_output("====================")
  93. self.print_output(" Saving to database ")
  94. self.print_output("====================")
  95. self.safe_bulk_create(objects_to_save)
  96. self.print_output("IPs from SFS saved to database")
  97. def safe_bulk_create(self, objs):
  98. """Wrapper to overcome the size limitation of standard bulk_create()"""
  99. if len(objs) == 0:
  100. return
  101. if 'sqlite' not in settings.DATABASES['default']['ENGINE']:
  102. objs[0].__class__.objects.bulk_create(objs)
  103. else:
  104. BULK_SIZE = 900 / len(objs[0].__class__._meta.fields)
  105. for i in range(0, len(objs), BULK_SIZE):
  106. self.print_output("SQLITE size limits: Bulk inserting %d objects" % BULK_SIZE)
  107. objs[0].__class__.objects.bulk_create(objs[i:i + BULK_SIZE])