diff --git a/remo/dashboard/views.py b/remo/dashboard/views.py index 5bb1d4ed0..f4b85d3d8 100644 --- a/remo/dashboard/views.py +++ b/remo/dashboard/views.py @@ -99,8 +99,9 @@ def dashboard(request): user = request.user args = {} - # Mozillians block - if user.groups.filter(name='Mozillians').exists(): + # Mozillians/Alumni block + if (user.groups.filter(name='Mozillians').exists() or + user.groups.filter(name='Alumni').exists()): return dashboard_mozillians(request, user) # Reps block diff --git a/remo/profiles/forms.py b/remo/profiles/forms.py index a7839e83d..1a44b4ea6 100644 --- a/remo/profiles/forms.py +++ b/remo/profiles/forms.py @@ -152,16 +152,33 @@ class Meta: 'functional_areas', 'timezone') -class ChangeDateJoinedForm(happyforms.ModelForm): - """Form to change userprofile date_joined_program field.""" - date_joined_program = forms.DateField( - required=False, - widget=SelectDateWidget(years=range(2011, now().date().year + 1), - required=False)) +class ChangeDatesForm(happyforms.ModelForm): + """Form to change the dates that user joined and left the program.""" class Meta: model = UserProfile - fields = ['date_joined_program'] + fields = ['date_joined_program', 'date_left_program'] + widgets = {'date_joined_program': + SelectDateWidget(years=range(2011, now().date().year + 1), + required=False), + 'date_left_program': + SelectDateWidget(years=range(now().date().year - 1, + now().date().year + 1), + required=False)} + + def save(self, commit=True): + """Override save method for custom functinality.""" + + # If a user belongs to the Alumni group and no date is suplied for + # leaving the program, the date is auto-populated. + # If a user is not member of the Alumni group the date_left_program + # must be None + if self.instance.user.groups.filter(name='Alumni').exists(): + if 'date_left_program' not in self.changed_data: + self.instance.date_left_program = now().date() + else: + self.instance.date_left_program = None + super(ChangeDatesForm, self).save() class FunctionalAreaForm(happyforms.ModelForm): diff --git a/remo/profiles/migrations/0051_auto__add_field_userprofile_date_left_program.py b/remo/profiles/migrations/0051_auto__add_field_userprofile_date_left_program.py new file mode 100644 index 000000000..125aacc02 --- /dev/null +++ b/remo/profiles/migrations/0051_auto__add_field_userprofile_date_left_program.py @@ -0,0 +1,131 @@ +# -*- coding: 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.date_left_program' + db.add_column('profiles_userprofile', 'date_left_program', + self.gf('django.db.models.fields.DateField')(null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'UserProfile.date_left_program' + db.delete_column('profiles_userprofile', 'date_left_program') + + + 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'}) + }, + 'profiles.functionalarea': { + 'Meta': {'ordering': "['name']", 'object_name': 'FunctionalArea'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'blank': 'True'}) + }, + 'profiles.useravatar': { + 'Meta': {'object_name': 'UserAvatar'}, + 'avatar_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '400'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_update': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 1, 27, 0, 0)', 'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}) + }, + 'profiles.userprofile': { + 'Meta': {'object_name': 'UserProfile'}, + 'added_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'users_added'", 'null': 'True', 'to': "orm['auth.User']"}), + 'bio': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'birth_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50'}), + 'country': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50'}), + 'current_streak_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'date_joined_program': ('django.db.models.fields.DateField', [], {'blank': 'True'}), + 'date_left_program': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'diaspora_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), + 'display_name': ('django.db.models.fields.CharField', [], {'default': "''", 'unique': 'True', 'max_length': '50', 'blank': 'True'}), + 'facebook_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), + 'first_report_notification': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'functional_areas': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'users_matching'", 'symmetrical': 'False', 'to': "orm['profiles.FunctionalArea']"}), + 'gender': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'irc_channels': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'irc_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50'}), + 'is_rotm_nominee': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_unavailable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'jabber_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}), + 'lat': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'linkedin_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), + 'local_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}), + 'lon': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'longest_streak_end': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'longest_streak_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'mentor': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mentees'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'mozillian_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40', 'blank': 'True'}), + 'mozillians_profile_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'personal_blog_feed': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), + 'personal_website_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), + 'private_email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '75', 'null': 'True'}), + 'receive_email_on_add_comment': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'receive_email_on_add_event_comment': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'receive_email_on_add_voting_comment': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'region': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50'}), + 'registration_complete': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'second_report_notification': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'timezone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}), + 'tracked_functional_areas': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'users_tracking'", 'symmetrical': 'False', 'to': "orm['profiles.FunctionalArea']"}), + 'twitter_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '16', 'blank': 'True'}), + 'unavailability_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}), + 'wiki_profile_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}) + }, + 'profiles.userstatus': { + 'Meta': {'ordering': "['-expected_date', '-created_on']", 'object_name': 'UserStatus'}, + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'expected_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'replacement_rep': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replaced_rep'", 'null': 'True', 'to': "orm['auth.User']"}), + 'return_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'status'", 'to': "orm['auth.User']"}) + } + } + + complete_apps = ['profiles'] \ No newline at end of file diff --git a/remo/profiles/migrations/0052_add_alumni_group.py b/remo/profiles/migrations/0052_add_alumni_group.py new file mode 100644 index 000000000..c61e5ea3a --- /dev/null +++ b/remo/profiles/migrations/0052_add_alumni_group.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + + +class Migration(DataMigration): + + def forwards(self, orm): + """Create Alumni group.""" + orm['auth.Group'].objects.create(name='Alumni') + + def backwards(self, orm): + """Delete Alumni Group.""" + orm['auth.Group'].objects.filter(name='Alumni').delete() + + 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'}) + }, + 'profiles.functionalarea': { + 'Meta': {'ordering': "['name']", 'object_name': 'FunctionalArea'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'blank': 'True'}) + }, + 'profiles.useravatar': { + 'Meta': {'object_name': 'UserAvatar'}, + 'avatar_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '400'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_update': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 1, 28, 0, 0)', 'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}) + }, + 'profiles.userprofile': { + 'Meta': {'object_name': 'UserProfile'}, + 'added_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'users_added'", 'null': 'True', 'to': "orm['auth.User']"}), + 'bio': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'birth_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50'}), + 'country': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50'}), + 'current_streak_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'date_joined_program': ('django.db.models.fields.DateField', [], {'blank': 'True'}), + 'date_left_program': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'diaspora_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), + 'display_name': ('django.db.models.fields.CharField', [], {'default': "''", 'unique': 'True', 'max_length': '50', 'blank': 'True'}), + 'facebook_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), + 'first_report_notification': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'functional_areas': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'users_matching'", 'symmetrical': 'False', 'to': "orm['profiles.FunctionalArea']"}), + 'gender': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'irc_channels': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'irc_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50'}), + 'is_rotm_nominee': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_unavailable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'jabber_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}), + 'lat': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'linkedin_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), + 'local_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}), + 'lon': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'longest_streak_end': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'longest_streak_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'mentor': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mentees'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'mozillian_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40', 'blank': 'True'}), + 'mozillians_profile_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'personal_blog_feed': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), + 'personal_website_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), + 'private_email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '75', 'null': 'True'}), + 'receive_email_on_add_comment': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'receive_email_on_add_event_comment': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'receive_email_on_add_voting_comment': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'region': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50'}), + 'registration_complete': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'second_report_notification': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'timezone': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}), + 'tracked_functional_areas': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'users_tracking'", 'symmetrical': 'False', 'to': "orm['profiles.FunctionalArea']"}), + 'twitter_account': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '16', 'blank': 'True'}), + 'unavailability_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}), + 'wiki_profile_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}) + }, + 'profiles.userstatus': { + 'Meta': {'ordering': "['-expected_date', '-created_on']", 'object_name': 'UserStatus'}, + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'expected_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'replacement_rep': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replaced_rep'", 'null': 'True', 'to': "orm['auth.User']"}), + 'return_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'status'", 'to': "orm['auth.User']"}) + } + } + + complete_apps = ['profiles'] + symmetrical = True diff --git a/remo/profiles/models.py b/remo/profiles/models.py index 0366ad30d..f8fc2521a 100644 --- a/remo/profiles/models.py +++ b/remo/profiles/models.py @@ -96,6 +96,7 @@ class UserProfile(caching.base.CachingMixin, models.Model): user = models.OneToOneField(User) registration_complete = models.BooleanField(default=False) date_joined_program = models.DateField(blank=True) + date_left_program = models.DateField(blank=True, null=True) local_name = models.CharField(max_length=100, blank=True, default='') birth_date = models.DateField(validators=[_validate_birth_date], blank=True, null=True) diff --git a/remo/profiles/static/profiles/js/edit_profile.js b/remo/profiles/static/profiles/js/edit_profile.js new file mode 100644 index 000000000..66da4f23b --- /dev/null +++ b/remo/profiles/static/profiles/js/edit_profile.js @@ -0,0 +1,15 @@ +(function($) { + 'use strict'; + + $('input:checkbox[id*="-bit"]').on('click', function() { + if ($(this).attr('id') === 'alumni-bit') { + if ($(this).is(':checked')){ + $('ul.edit-profile-item li input:checkbox[id*="-bit"]').not($(this)).prop('checked', false); + } + } + else { + $('input:checkbox[id="alumni-bit"]').prop('checked', false); + } + }); + +})(jQuery); diff --git a/remo/profiles/templates/profiles_edit.html b/remo/profiles/templates/profiles_edit.html index c61c0e31d..3482e45fe 100644 --- a/remo/profiles/templates/profiles_edit.html +++ b/remo/profiles/templates/profiles_edit.html @@ -6,7 +6,7 @@
{{ csrf() }}
- {% if profileform.errors or userform.errors or datejoinedform.errors %} + {% if profileform.errors or userform.errors or profile_date_form.errors %}
Please correct the errors below.
@@ -502,12 +502,21 @@

Date Rep joined program
- {{ datejoinedform.date_joined_program }} - {% if datejoinedform.date_joined_program.errors %} + {{ profile_date_form.date_joined_program }} + {% if profile_date_form.date_joined_program.errors %} - {{ datejoinedform.date_joined_program.errors }} + {{ profile_date_form.date_joined_program.errors }} {% endif %} + {% if user_is_alumni %} +
Date Rep left program
+ {{ profile_date_form.date_left_program }} + {% if profile_date_form.date_left_program.errors %} + + {{ profile_date_form.date_left_program.errors }} + + {% endif %} + {% endif %}
@@ -543,6 +552,13 @@
Date Rep joined program
Rep +
  • + +
  • @@ -631,5 +647,6 @@

    + {% endcompress %} {% endblock %} diff --git a/remo/profiles/tests/test_views.py b/remo/profiles/tests/test_views.py index 156dfc31e..1022d86b1 100644 --- a/remo/profiles/tests/test_views.py +++ b/remo/profiles/tests/test_views.py @@ -274,7 +274,7 @@ def test_functional_areas_type(self): class RotmAutomationTests(RemoTestCase): """Tests related to the Rep of the month automation view.""" - @mock.patch('remo.profiles.views.timezone.now') + @mock.patch('remo.profiles.views.now') @mock.patch('remo.profiles.views.forms.RotmNomineeForm') def test_base(self, mocked_form, mocked_date): mocked_date.return_value = datetime(now().year, now().month, 4) @@ -292,7 +292,7 @@ def test_base(self, mocked_form, mocked_date): self.assertTemplateUsed('profiles_view_profile.html') @mock.patch('remo.profiles.views.messages.warning') - @mock.patch('remo.profiles.views.timezone.now') + @mock.patch('remo.profiles.views.now') @mock.patch('remo.profiles.views.forms.RotmNomineeForm') def test_no_mentor(self, mocked_form, mocked_date, mocked_message): mocked_date.return_value = datetime(now().year, now().month, 4) @@ -309,7 +309,7 @@ def test_no_mentor(self, mocked_form, mocked_date, mocked_message): mocked_message.assert_called_with( mock.ANY, 'Only mentors can nominate a mentee.') - @mock.patch('remo.profiles.views.timezone.now') + @mock.patch('remo.profiles.views.now') @mock.patch('remo.profiles.views.forms.RotmNomineeForm') def test_invalid_period(self, mocked_form, mocked_date): mocked_date.return_value = datetime(now().year, now().month, 25) diff --git a/remo/profiles/views.py b/remo/profiles/views.py index c515a01cf..cbac991a2 100644 --- a/remo/profiles/views.py +++ b/remo/profiles/views.py @@ -1,12 +1,13 @@ from django.conf import settings from django.contrib import messages +from django.contrib.auth.decorators import user_passes_test from django.contrib.auth.models import Group, User from django.core.urlresolvers import reverse from django.db.models import Q from django.http import Http404 from django.shortcuts import get_object_or_404, redirect, render from django.template.loader import render_to_string -from django.utils import timezone +from django.utils.timezone import now from django.utils.encoding import iri_to_uri from django.utils.safestring import mark_safe from django.views.decorators.cache import cache_control, never_cache @@ -30,6 +31,8 @@ @never_cache +@user_passes_test(lambda u: u.groups.filter(Q(name='Rep') | Q(name='Admin')), + login_url=settings.LOGIN_REDIRECT_URL) @permission_check(permissions=['profiles.can_edit_profiles'], filter_field='display_name', owner_field='user', model=UserProfile) @@ -45,7 +48,7 @@ def edit(request, display_name): """ - def date_joined_form_validation(form): + def profile_date_form_validation(form): """Convenience function to only validate datejoinedform when user has permissions. @@ -62,29 +65,31 @@ def date_joined_form_validation(form): userform = forms.ChangeUserForm(request.POST or None, instance=user) profileform = forms.ChangeProfileForm(request.POST or None, instance=user.userprofile) - datejoinedform = forms.ChangeDateJoinedForm(request.POST or None, - instance=user.userprofile) + profile_date_form = forms.ChangeDatesForm(request.POST or None, + instance=user.userprofile) if (userform.is_valid() and profileform.is_valid() and - date_joined_form_validation(datejoinedform)): + profile_date_form_validation(profile_date_form)): userform.save() profileform.save() if request.user.has_perm('profiles.can_edit_profiles'): - # Update date joined - datejoinedform.save() - # Update groups. groups = {'Mentor': 'mentor_group', 'Admin': 'admin_group', 'Council': 'council_group', - 'Rep': 'rep_group'} + 'Rep': 'rep_group', + 'Alumni': 'alumni_group'} for group_db, group_html in groups.items(): - if request.POST.get(group_html, None): - user.groups.add(Group.objects.get(name=group_db)) - else: - user.groups.remove(Group.objects.get(name=group_db)) + if Group.objects.filter(name=group_db).exists(): + if request.POST.get(group_html, None): + user.groups.add(Group.objects.get(name=group_db)) + else: + user.groups.remove(Group.objects.get(name=group_db)) + + # Update date fields + profile_date_form.save() messages.success(request, 'Profile successfully edited.') statsd.incr('profiles.edit_profile') @@ -98,20 +103,20 @@ def date_joined_form_validation(form): return redirect(redirect_url) group_bits = map(lambda x: user.groups.filter(name=x).exists(), - ['Admin', 'Council', 'Mentor', 'Rep']) - - pageuser = get_object_or_404(User, userprofile__display_name=display_name) + ['Admin', 'Council', 'Mentor', 'Rep', 'Alumni']) functional_areas = map(int, profileform['functional_areas'].value()) + user_is_alumni = user.groups.filter(name='Alumni').exists() return render(request, 'profiles_edit.html', {'userform': userform, 'profileform': profileform, - 'datejoinedform': datejoinedform, - 'pageuser': pageuser, + 'profile_date_form': profile_date_form, + 'pageuser': user, 'group_bits': group_bits, - 'range_years': range(1950, timezone.now().date().year - 11), - 'functional_areas': functional_areas}) + 'range_years': range(1950, now().date().year - 11), + 'functional_areas': functional_areas, + 'user_is_alumni': user_is_alumni}) def redirect_list_profiles(request): @@ -142,7 +147,7 @@ def view_profile(request, display_name): """View user profile.""" user = get_object_or_404(User, userprofile__display_name__iexact=display_name) - if not user.groups.filter(name='Rep').exists(): + if not user.groups.filter(Q(name='Rep') | Q(name='Alumni')).exists(): raise Http404 if (not user.userprofile.registration_complete and @@ -153,7 +158,7 @@ def view_profile(request, display_name): instance=user.userprofile) usergroups = user.groups.filter(Q(name='Mentor') | Q(name='Council')) - is_nomination_period = timezone.now().date() < rotm_nomination_end_date() + is_nomination_period = now().date() < rotm_nomination_end_date() data = {'pageuser': user, 'user_profile': user.userprofile, 'added_by': user.userprofile.added_by, @@ -166,7 +171,7 @@ def view_profile(request, display_name): status = UserStatus.objects.filter(user=user).latest('created_on') data['user_status'] = status if user == request.user: - today = timezone.now().date() + today = now().date() date = (status.expected_date.strftime('%d %B %Y') if status.expected_date > today else None) msg = render_to_string( @@ -183,7 +188,7 @@ def view_profile(request, display_name): return redirect('profiles_view_profile', display_name=display_name) messages.warning(request, ('Only mentors can nominate a mentee.')) - today = timezone.now().date() + today = now().date() # NGReports data['ng_reports'] = (user.ng_reports