From bf3893768c42e7c0d4f4205819ef4e5ada1b2707 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 8 Jan 2011 11:05:10 -0600 Subject: Add pytz as a project requirement Signed-off-by: Dan McGee --- requirements.txt | 1 + requirements_prod.txt | 1 + 2 files changed, 2 insertions(+) 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 -- cgit v1.2.3 From 9a1971e24fe15ccdb74f3a8a59d735504f2797be Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 8 Jan 2011 11:41:59 -0600 Subject: Add time zone field to developer profile This will be used by the developer world clock page soon to come. Default everyone to "UTC" for now. Signed-off-by: Dan McGee --- .../0037_auto__add_field_userprofile_time_zone.py | 154 +++++++++++++++++++++ main/models.py | 9 +- 2 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 main/migrations/0037_auto__add_field_userprofile_time_zone.py 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 8858b17b..0e9e55a1 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.all_timezones), + default="UTC", + help_text="Used for developer clock page") alias = models.CharField( max_length=50, help_text="Required field") -- cgit v1.2.3 From c04d4abc88cdf0891bff2b1ce6d1d746c5b9b3c9 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 8 Jan 2011 11:47:11 -0600 Subject: Add developer world clocks page This should make it easier for everyone to figure out whether someone is awake, sleeping, and all that fun stuff. It does require everyone to update their profile and fill in the field, but that shouldn't be too hard of a task. Don't suggest jokes to me Saturday morning on IRC unless you really want to see them implemented. Thanks, Pierre! Signed-off-by: Dan McGee --- devel/urls.py | 1 + devel/views.py | 25 ++++++++++++++++++++++- templates/devel/clock.html | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 templates/devel/clock.html 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/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 %} +
+

Developer World Clocks

+ +

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.

+

+ Arch Server Time: {{ now|date:"Y-m-d H:i" }}
+ UTC Time: {{ utc_now|date:"Y-m-d H:i" }} +

+ + + + + + + + + + + + + {% for dev in developers %} + + + + + + + + {% endfor %} + +
DeveloperUsernameLocationTime ZoneCurrent Time
{{ dev.get_full_name }}{{ dev.username }}{{ dev.userprofile.location }}{{ dev.userprofile.time_zone }}{{ dev.current_time|date:"Y-m-d H:i" }}
+
+{% load cdn %}{% jquery %} + + + +{% endblock %} -- cgit v1.2.3