Changeset 7ef7143


Ignore:
Timestamp:
Jan 22, 2012, 10:48:51 AM (14 years ago)
Author:
Alex Dehnert <adehnert@…>
Branches:
master, space-access, stable, stage
Children:
fb1c047
Parents:
0c4be73
git-author:
Alex Dehnert <adehnert@…> (01/19/12 05:20:05)
git-committer:
Alex Dehnert <adehnert@…> (01/22/12 10:48:51)
Message:

Regularly download constitutions (Trac #50)

Adds groups/gather_constitutions.py and associated support code to regularly
download group constitutions and track them.

This will not treat 404ing or 500ing constitutions as an error.

Files:
2 added
3 edited

Legend:

Unmodified
Added
Removed
  • .gitignore

    r728cafa r7ef7143  
    1010asadb/util/static-data/
    1111asadb/util/warehouse-dump.csv
     12constitutions
  • asadb/groups/admin.py

    rac93a0c r7ef7143  
    2626    search_fields = [ 'id', 'name', 'abbreviation', 'officer_email', 'athena_locker', ]
    2727admin.site.register(groups.models.Group, GroupAdmin)
     28
     29
     30class Admin_GroupConstitution(VersionAdmin):
     31    list_display = (
     32        'pk',
     33        'group',
     34        'source_url',
     35        'dest_file',
     36        'last_update',
     37        'last_download',
     38        'failure_date',
     39    )
     40admin.site.register(groups.models.GroupConstitution, Admin_GroupConstitution)
    2841
    2942
  • asadb/groups/models.py

    r41146ff r7ef7143  
    11from django.db import models
    22from django.contrib.auth.models import User
     3from django.template.defaultfilters import slugify
    34import reversion
    45
    56import datetime
     7import filecmp
     8import mimetypes
     9import os
     10import re
     11import shutil
     12import urllib
    613
    714import settings
     
    8592        return office_holders
    8693
     94    def slug(self, ):
     95        return slugify(self.name)
     96
    8797    def __str__(self, ):
    8898        return self.name
     
    111121    (GROUP_STARTUP_STAGE_REJECTED,      'rejected'),
    112122)
     123
     124constitution_dir = os.path.join(settings.SITE_ROOT, '..', 'constitutions')
     125
     126class GroupConstitution(models.Model):
     127    group = models.ForeignKey(Group, unique=True, )
     128    source_url = models.URLField()
     129    dest_file = models.CharField(max_length=100)
     130    last_update = models.DateTimeField(help_text='Last time when this constitution actually changed.')
     131    last_download = models.DateTimeField(help_text='Last time we downloaded this constitution to see if it had changed.')
     132    failure_date = models.DateTimeField(null=True, blank=True, default=None, help_text='Time this URL started failing to download. (Null if currently working.)')
     133    status_msg = models.CharField(max_length=20)
     134    failure_reason = models.CharField(max_length=100, blank=True, default="")
     135
     136    def update(self, ):
     137        url = self.source_url
     138        success = None
     139        old_success = (self.failure_date is None)
     140        if url:
     141            url_opener = urllib.FancyURLopener()
     142            now = datetime.datetime.now()
     143            try:
     144                tmp_path, headers = url_opener.retrieve(url)
     145            except IOError:
     146                self.failure_date = now
     147                self.save()
     148                success = False
     149                self.status_msg = "retrieval failed"
     150                self.failure_reason = self.status_msg
     151                return (success, self.status_msg, old_success, )
     152            if tmp_path == url:
     153                mover = shutil.copyfile
     154            else:
     155                mover = shutil.move
     156            save_filename = self.compute_filename(tmp_path, headers, )
     157            dest_path = self.path_from_filename(self.dest_file)
     158            if save_filename != self.dest_file:
     159                if self.dest_file: os.remove(dest_path)
     160                mover(tmp_path, self.path_from_filename(save_filename))
     161                self.dest_file = save_filename
     162                self.last_update = now
     163                self.status_msg = "new path"
     164            else:
     165                if filecmp.cmp(tmp_path, dest_path, shallow=False, ):
     166                    self.status_msg = "no change"
     167                else:
     168                    # changed
     169                    mover(tmp_path, dest_path)
     170                    self.last_update = now
     171                    self.status_msg = "updated in place"
     172            self.last_download = now
     173            self.failure_date = None
     174            self.failure_reason = ""
     175            self.save()
     176            success = True
     177        else:
     178            success = False
     179            self.status_msg = "no url"
     180            self.failure_reason = self.status_msg
     181        return (success, self.status_msg, old_success, )
     182
     183    def compute_filename(self, tmp_path, headers, ):
     184        slug = self.group.slug()
     185        basename, fileext = os.path.splitext(tmp_path)
     186        if fileext:
     187            ext = fileext
     188        else:
     189            if headers.getheader('Content-Type'):
     190                mimeext = mimetypes.guess_extension(headers.gettype())
     191                if mimeext:
     192                    ext = mimeext
     193                else:
     194                    ext = ''
     195            else:
     196                ext = ''
     197        return "%04d-%s%s" % (self.group.pk, slug, ext, )
     198
     199    def path_from_filename(self, filename):
     200        path = os.path.join(constitution_dir, filename)
     201        return path
     202
     203reversion.register(GroupConstitution)
     204
    113205
    114206class GroupStartup(models.Model):
Note: See TracChangeset for help on using the changeset viewer.