From 5c168b231a814c033264326b39084f184c06e0c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Reynolds?= Date: Sun, 9 Jan 2011 17:52:29 -0300 Subject: more merge --- README | 24 ++++++++++++++---------- devel/urls.py | 1 + devel/views.py | 25 ++++++++++++++++++++++++- main/models.py | 17 +++++++---------- media/archweb.js | 22 +++++++++++++++++++--- media/jquery.tablesorter.min.js | 2 +- news/models.py | 1 - requirements.txt | 1 + requirements_prod.txt | 1 + settings.py | 23 ++++++----------------- templates/base.html | 3 ++- 11 files changed, 76 insertions(+), 44 deletions(-) diff --git a/README b/README index dc43e9da..6b492d84 100644 --- a/README +++ b/README @@ -33,37 +33,41 @@ packages, you will probably want the following: 1. Run `virtualenv`. - $ cd /path/to/archweb && virtualenv ../archweb + $ cd /path/to/archweb && virtualenv ../archweb-env + +2. Source the virtualenv. + + $ . ../archweb-env/bin/activate 2. Install dependencies through `pip`. - $ pip install -r requirements.txt + (archweb-env) $ pip install -r requirements.txt 3. Copy `local_settings.py.example` to `local_settings.py` and modify. Make sure to uncomment the appropriate db section (either sqlite or mysql). 4. Sync the database to create it. - $ ./manage.py syncdb + (archweb-env) $ ./manage.py syncdb 5. Migrate changes. - $ ./manage.py migrate + (archweb-env) $ ./manage.py migrate 6. Load the fixtures to prepopulate some data. - $ ./manage.py loaddata main/fixtures/arches.json - # ./manage.py loaddata main/fixtures/repos.json - # ./manage.py loaddata mirrors/fixtures/mirrorprotocols.json + (archweb-env) $ ./manage.py loaddata main/fixtures/arches.json + (archweb-env) $ ./manage.py loaddata main/fixtures/repos.json + (archweb-env) $ ./manage.py loaddata mirrors/fixtures/mirrorprotocols.json 7. Use the following commands to start a service instance - $ ./manage.py runserver + (archweb-env) $ ./manage.py runserver 8. To optionally populate the database with real data: - $ wget ftp://ftp.archlinux.org/core/os/i686/core.db.tar.gz - $ ./manage.py reporead i686 core.db.tar.gz + (archweb-env) $ wget ftp://ftp.archlinux.org/core/os/i686/core.db.tar.gz + (archweb-env) $ ./manage.py reporead i686 core.db.tar.gz Alter architecture and repo to get x86\_64 and packages from other repos if needed. diff --git a/devel/urls.py b/devel/urls.py index 23dd2d9f..0a050a92 100644 --- a/devel/urls.py +++ b/devel/urls.py @@ -2,6 +2,7 @@ from django.conf.urls.defaults import patterns urlpatterns = patterns('devel.views', (r'^$', 'index'), + (r'^clock/$', 'clock'), (r'^notify/$', 'change_notify'), (r'^profile/$', 'change_profile'), (r'^newuser/$', 'new_user_form'), diff --git a/devel/views.py b/devel/views.py index 710bfff5..b26c7af0 100644 --- a/devel/views.py +++ b/devel/views.py @@ -13,6 +13,8 @@ from main.models import UserProfile from packages.models import PackageRelation from .utils import get_annotated_maintainers +import datetime +import pytz import random from string import ascii_letters, digits @@ -38,10 +40,31 @@ def index(request): 'maintainers': maintainers, 'flagged' : flagged, 'todopkgs' : todopkgs, - } + } return direct_to_template(request, 'devel/index.html', page_dict) +@login_required +@never_cache +def clock(request): + devs = User.objects.filter(is_active=True).order_by( + 'username').select_related('userprofile') + + # now annotate each dev object with their current time + now = datetime.datetime.now() + utc_now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) + for dev in devs: + tz = pytz.timezone(dev.userprofile.time_zone) + dev.current_time = utc_now.astimezone(tz) + + page_dict = { + 'developers': devs, + 'now': now, + 'utc_now': utc_now, + } + + return direct_to_template(request, 'devel/clock.html', page_dict) + @login_required def change_notify(request): maint = User.objects.get(username=request.user.username) diff --git a/main/models.py b/main/models.py index 8858b17b..ff2ecf02 100644 --- a/main/models.py +++ b/main/models.py @@ -2,18 +2,23 @@ from django.db import models from django.contrib.auth.models import User from django.contrib.sites.models import Site -from main.utils import cache_function +from main.utils import cache_function, make_choice from packages.models import PackageRelation from itertools import groupby +import pytz from operator import attrgetter class UserProfile(models.Model): - id = models.AutoField(primary_key=True) # not technically needed notify = models.BooleanField( "Send notifications", default=True, help_text="When enabled, send user 'flag out-of-date' notifications") + time_zone = models.CharField( + max_length=100, + choices=make_choice(pytz.common_timezones), + default="UTC", + help_text="Used for developer clock page") alias = models.CharField( max_length=50, help_text="Required field") @@ -49,7 +54,6 @@ class PackageManager(models.Manager): class Donor(models.Model): - id = models.AutoField(primary_key=True) name = models.CharField(max_length=255, unique=True) visible = models.BooleanField(default=True, help_text="Should we show this donor on the public page?") @@ -62,7 +66,6 @@ class Donor(models.Model): ordering = ['name'] class Arch(models.Model): - id = models.AutoField(primary_key=True) name = models.CharField(max_length=255, unique=True) agnostic = models.BooleanField(default=False, help_text="Is this architecture non-platform specific?") @@ -79,7 +82,6 @@ class Arch(models.Model): verbose_name_plural = 'arches' class Repo(models.Model): - id = models.AutoField(primary_key=True) name = models.CharField(max_length=255, unique=True) testing = models.BooleanField(default=False, help_text="Is this repo meant for package testing?") @@ -100,7 +102,6 @@ class Repo(models.Model): verbose_name_plural = 'repos' class Package(models.Model): - id = models.AutoField(primary_key=True) repo = models.ForeignKey(Repo, related_name="packages") arch = models.ForeignKey(Arch, related_name="packages") pkgname = models.CharField(max_length=255, db_index=True) @@ -308,14 +309,12 @@ class Signoff(models.Model): packager = models.ForeignKey(User) class PackageFile(models.Model): - id = models.AutoField(primary_key=True) pkg = models.ForeignKey('Package') path = models.CharField(max_length=255) class Meta: db_table = 'package_files' class PackageDepend(models.Model): - id = models.AutoField(primary_key=True) pkg = models.ForeignKey('Package') depname = models.CharField(db_index=True, max_length=255) depvcmp = models.CharField(max_length=255) @@ -323,7 +322,6 @@ class PackageDepend(models.Model): db_table = 'package_depends' class Todolist(models.Model): - id = models.AutoField(primary_key=True) creator = models.ForeignKey(User) name = models.CharField(max_length=255) description = models.TextField() @@ -351,7 +349,6 @@ class Todolist(models.Model): return '/todo/%i/' % self.id class TodolistPkg(models.Model): - id = models.AutoField(primary_key=True) list = models.ForeignKey('Todolist') pkg = models.ForeignKey('Package') complete = models.BooleanField(default=False) diff --git a/media/archweb.js b/media/archweb.js index c5025ded..1c80ab64 100644 --- a/media/archweb.js +++ b/media/archweb.js @@ -36,18 +36,34 @@ if(typeof $.tablesorter !== "undefined") { $.tablesorter.addParser({ /* sorts duration; put '', 'unknown', and '∞' last. */ id: 'duration', - is: function(s,table) { + re: /^([0-9]+):([0-5][0-9])$/, + is: function(s) { var special = ['', 'unknown', '∞']; - return ($.inArray(s, special) > -1) || /^[0-9]+:[0-5][0-9]$/.test(s); + return ($.inArray(s, special) > -1) || this.re.test(s); }, format: function(s) { var special = ['', 'unknown', '∞']; if($.inArray(s, special) > -1) return Number.MAX_VALUE; - matches = /^([0-9]+):([0-5][0-9])$/.exec(s); + var matches = this.re.exec(s); return matches[1] * 60 + matches[2]; }, type: 'numeric' }); + $.tablesorter.addParser({ + id: 'longDateTime', + re: /^(\d{4})-(\d{2})-(\d{2}) ([012]\d):([0-5]\d)(:([0-5]\d))?( (\w+))?$/, + is: function (s) { + return this.re.test(s); + }, + format: function (s) { + var matches = this.re.exec(s); + /* skip group 6, group 7 is optional seconds */ + if(matches[7] == undefined) matches[7] = "0"; + return $.tablesorter.formatFloat(new Date( + matches[1],matches[2],matches[3],matches[4],matches[5],matches[7]).getTime()); + }, + type: "numeric" + }); } /* news/add.html */ diff --git a/media/jquery.tablesorter.min.js b/media/jquery.tablesorter.min.js index 64c70071..7d54b3bc 100644 --- a/media/jquery.tablesorter.min.js +++ b/media/jquery.tablesorter.min.js @@ -1,2 +1,2 @@ -(function($){$.extend({tablesorter:new function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'.',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}var rows=table.tBodies[0].rows;if(table.tBodies[0].rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;ib)?1:0));};function sortTextDesc(a,b){return((ba)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;ib)?1:0));};function sortTextDesc(a,b){return((ba)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i=2010o diff --git a/requirements_prod.txt b/requirements_prod.txt index 49ca44c4..b8665f35 100644 --- a/requirements_prod.txt +++ b/requirements_prod.txt @@ -3,3 +3,4 @@ Markdown==2.0.3 MySQL-python==1.2.3c1 South==0.7.3 python-memcached==1.47 +pytz>=2010o diff --git a/settings.py b/settings.py index f98f709c..8e916e36 100644 --- a/settings.py +++ b/settings.py @@ -6,9 +6,7 @@ DEBUG = False TEMPLATE_DEBUG = DEBUG ## Notification admins -ADMINS = ( - ('Dan McGee', 'dan@archlinux.org'), -) +ADMINS = () # Set managers to admins MANAGERS = ADMINS @@ -31,14 +29,6 @@ DEFAULT_CHARSET = 'utf-8' SITE_ID = 1 -# If you set this to False, Django will make some optimizations so as not -# to load the internationalization machinery. -USE_I18N = False - -# If you set this to False, Django will not format dates, numbers and -# calendars according to the current locale -USE_L10N = False - # Default date format in templates for 'date' filter DATE_FORMAT = 'Y-m-d' @@ -47,10 +37,13 @@ DATE_FORMAT = 'Y-m-d' # Examples: "http://foo.com/media/", "/media/". ADMIN_MEDIA_PREFIX = '/media/admin_media/' -# login url +# Login URL configuration LOGIN_URL = '/login/' LOGIN_REDIRECT_URL = '/' +# Set django's User stuff to use our profile model +AUTH_PROFILE_MODULE = 'main.UserProfile' + # List of callables that know how to import templates from various sources. TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.load_template_source', @@ -96,12 +89,8 @@ TEMPLATE_LOADERS = ( 'django.template.loaders.app_directories.Loader', ) -# Set django's User stuff to use our profile model -# format is app.model -AUTH_PROFILE_MODULE = 'main.UserProfile' - +# Configure where sessions and messages should reside MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage' - SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' INSTALLED_APPS = ( diff --git a/templates/base.html b/templates/base.html index 98e2da4e..8a501821 100644 --- a/templates/base.html +++ b/templates/base.html @@ -18,7 +18,7 @@
@@ -39,6 +39,7 @@
  • Archives
  • +
  • Dev Clocks
  • Profile
  • Logout
  • -- cgit v1.2.3-2-g168b