summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan McGee <dan@archlinux.org>2011-01-08 11:49:41 -0600
committerDan McGee <dan@archlinux.org>2011-01-08 11:49:41 -0600
commite927df1a643e2c0df103ca0fe027ef295e3975e9 (patch)
tree7cc4405aa56f67b9b5da8786506186cab68bb61d
parentc18cd21e536592b6ed482126ee423f38b9ac9257 (diff)
parentc04d4abc88cdf0891bff2b1ce6d1d746c5b9b3c9 (diff)
Merge branch 'dev-timezones'
-rw-r--r--devel/urls.py1
-rw-r--r--devel/views.py25
-rw-r--r--main/migrations/0037_auto__add_field_userprofile_time_zone.py154
-rw-r--r--main/models.py8
-rw-r--r--requirements.txt1
-rw-r--r--requirements_prod.txt1
-rw-r--r--templates/devel/clock.html49
7 files changed, 237 insertions, 2 deletions
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,11 +40,32 @@ 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)
notify = request.POST.get('notify', 'no')
diff --git a/main/migrations/0037_auto__add_field_userprofile_time_zone.py b/main/migrations/0037_auto__add_field_userprofile_time_zone.py
new file mode 100644
index 00000000..9b9b8beb
--- /dev/null
+++ b/main/migrations/0037_auto__add_field_userprofile_time_zone.py
@@ -0,0 +1,154 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'UserProfile.time_zone'
+ db.add_column('user_profiles', 'time_zone', self.gf('django.db.models.fields.CharField')(default='UTC', max_length=100), keep_default=False)
+
+ def backwards(self, orm):
+ # Deleting field 'UserProfile.time_zone'
+ db.delete_column('user_profiles', 'time_zone')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'main.arch': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Arch', 'db_table': "'arches'"},
+ 'agnostic': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'main.donor': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Donor', 'db_table': "'donors'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'main.package': {
+ 'Meta': {'ordering': "('pkgname',)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'to': "orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'files_last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'flag_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'installed_size': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'to': "orm['main.Repo']"}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ 'main.packagedepend': {
+ 'Meta': {'object_name': 'PackageDepend', 'db_table': "'package_depends'"},
+ 'depname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'depvcmp': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"})
+ },
+ 'main.packagefile': {
+ 'Meta': {'object_name': 'PackageFile', 'db_table': "'package_files'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'path': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"})
+ },
+ 'main.repo': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'Repo', 'db_table': "'repos'"},
+ 'bugs_project': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'svn_root': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'testing': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'main.signoff': {
+ 'Meta': {'object_name': 'Signoff'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}),
+ 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'main.todolist': {
+ 'Meta': {'object_name': 'Todolist', 'db_table': "'todolists'"},
+ 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'date_added': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'main.todolistpkg': {
+ 'Meta': {'unique_together': "(('list', 'pkg'),)", 'object_name': 'TodolistPkg', 'db_table': "'todolist_pkgs'"},
+ 'complete': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Todolist']"}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"})
+ },
+ 'main.userprofile': {
+ 'Meta': {'object_name': 'UserProfile', 'db_table': "'user_profiles'"},
+ 'alias': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'allowed_repos': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Repo']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'favorite_distros': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'interests': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'languages': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'notify': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'occupation': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'other_contact': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'picture': ('django.db.models.fields.files.FileField', [], {'default': "'devs/silhouette.png'", 'max_length': '100'}),
+ 'public_email': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'roles': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'time_zone': ('django.db.models.fields.CharField', [], {'default': "'UTC'", 'max_length': '100'}),
+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'userprofile'", 'unique': 'True', 'to': "orm['auth.User']"}),
+ 'website': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'yob': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['main']
diff --git a/main/models.py b/main/models.py
index 61282153..7ce2d2f4 100644
--- a/main/models.py
+++ b/main/models.py
@@ -2,10 +2,11 @@ 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):
@@ -13,6 +14,11 @@ class UserProfile(models.Model):
"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.all_timezones),
+ default="UTC",
+ help_text="Used for developer clock page")
alias = models.CharField(
max_length=50,
help_text="Required field")
diff --git a/requirements.txt b/requirements.txt
index f4d80eeb..ba21a59a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
Django==1.2.4
Markdown==2.0.3
South==0.7.3
+pytz>=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/templates/devel/clock.html b/templates/devel/clock.html
new file mode 100644
index 00000000..ec567c2d
--- /dev/null
+++ b/templates/devel/clock.html
@@ -0,0 +1,49 @@
+{% extends "base.html" %}
+
+{% block title %}Arch Linux - Developer World Clocks{% endblock %}
+
+{% block content %}
+<div id="dev-clocks-box" class="box">
+ <h2>Developer World Clocks</h2>
+
+ <p>This page helps prevent you from waking a sleeping developer. It also
+ depends on developers keeping the time zone information up to date, so if
+ you see 'UTC' listed, pester them to update their settings.</p>
+ <p>
+ Arch Server Time: {{ now|date:"Y-m-d H:i" }}<br/>
+ UTC Time: {{ utc_now|date:"Y-m-d H:i" }}
+ </p>
+
+ <table id="clocks-table" class="results dash-stats">
+ <thead>
+ <tr>
+ <th>Developer</th>
+ <th>Username</th>
+ <th>Location</th>
+ <th>Time Zone</th>
+ <th>Current Time</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for dev in developers %}
+ <tr class="{% cycle 'odd' 'even' %}">
+ <td>{{ dev.get_full_name }}</td>
+ <td>{{ dev.username }}</td>
+ <td>{{ dev.userprofile.location }}</td>
+ <td>{{ dev.userprofile.time_zone }}</td>
+ <td>{{ dev.current_time|date:"Y-m-d H:i" }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+</div>
+{% load cdn %}{% jquery %}
+<script type="text/javascript" src="/media/jquery.tablesorter.min.js"></script>
+<script type="text/javascript" src="/media/archweb.js"></script>
+<script type="text/javascript">
+$(document).ready(function() {
+ $("#clocks-table:has(tbody tr)").tablesorter(
+ {widgets: ['zebra'], sortList: [[1,0]]});
+});
+</script>
+{% endblock %}