Browse files

Initial import.

  • Loading branch information...
0 parents commit 50d4813c687e239d965b34430602f23ca4b57906 @hop hop committed Mar 21, 2012
Showing with 12,572 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +9 −0 README.rst
  3. 0 __init__.py
  4. +28 −0 admin.py
  5. 0 announce/__init__.py
  6. +6 −0 announce/urls.py
  7. +65 −0 announce/views.py
  8. 0 billing/__init__.py
  9. +25 −0 billing/models.py
  10. +12 −0 cal/__init__.py
  11. +14 −0 cal/feeds.py
  12. +27 −0 cal/fields.py
  13. +19 −0 cal/forms.py
  14. +161 −0 cal/models.py
  15. 0 cal/templatetags/__init__.py
  16. +33 −0 cal/templatetags/cal_tags.py
  17. +90 −0 cal/tests.py
  18. +75 −0 cal/urls.py
  19. +203 −0 cal/views.py
  20. +19 −0 cal/widgets.py
  21. 0 core/__init__.py
  22. +36 −0 core/context_processors.py
  23. +46 −0 core/middleware.py
  24. +29 −0 core/models.py
  25. 0 core/templatetags/__init__.py
  26. +42 −0 core/templatetags/makeform.py
  27. +19 −0 core/utils.py
  28. +15 −0 domail.py
  29. +27 −0 import_payment.py
  30. +26 −0 list_intern_emails.py
  31. +26 −0 list_intern_emails_new.py
  32. +22 −0 manage.py
  33. 0 members/__init__.py
  34. +41 −0 members/forms.py
  35. 0 members/management/__init__.py
  36. 0 members/management/commands/__init__.py
  37. +33 −0 members/management/commands/import_payment.py
  38. +412 −0 members/models.py
  39. +31 −0 members/urls.py
  40. +75 −0 members/util.py
  41. +152 −0 members/views.py
  42. +3 −0 metalab/media/gallerypics/.gitignore
  43. BIN metalab/media/images/atomic.gif
  44. BIN metalab/media/images/bg.gif
  45. BIN metalab/media/images/default_avatar.png
  46. BIN metalab/media/images/fail.jpg
  47. BIN metalab/media/images/logo.gif
  48. BIN metalab/media/images/logo.png
  49. BIN metalab/media/images/logo_trauer.png
  50. BIN metalab/media/images/metasense_off.gif
  51. BIN metalab/media/images/metasense_on.gif
  52. BIN metalab/media/images/pixel.gif
  53. BIN metalab/media/images/pixel_blue.png
  54. BIN metalab/media/images/soup_logo.png
  55. +263 −0 metalab/media/javascripts/DateTimeShortcuts.js
  56. +119 −0 metalab/media/javascripts/ml-ajaxtools.js
  57. +4,184 −0 metalab/media/javascripts/prototype.js
  58. +13 −0 metalab/media/stylesheets/base.css
  59. +122 −0 metalab/media/stylesheets/cal.css
  60. +117 −0 metalab/media/stylesheets/cellardoor.css
  61. +56 −0 metalab/media/stylesheets/global.css
  62. +312 −0 metalab/media/stylesheets/layout.css
  63. +34 −0 metalab/media/stylesheets/members.css
  64. BIN metalab/media/stylesheets/metanight.png
  65. 0 metalab/media/stylesheets/monobook.css
  66. +12 −0 metalab/media/stylesheets/projects.css
  67. +53 −0 metalab/media/stylesheets/reset.css
  68. +76 −0 metalab/media/stylesheets/soup_startpage.css
  69. +1,635 −0 metalab/media/stylesheets/wikipage.css
  70. 0 metalab/media/usbherelist/herelist.txt
  71. +3 −0 metalab/media/userpics/.gitignore
  72. +1 −0 metalab/templates/404.html
  73. +3 −0 metalab/templates/500.html
  74. +47 −0 metalab/templates/admin/base.html
  75. +31 −0 metalab/templates/announce/message_sent.html
  76. +21 −0 metalab/templates/announce/write_message.html
  77. +8 −0 metalab/templates/auth/user_list.html
  78. +14 −0 metalab/templates/auth/user_list.inc
  79. +3 −0 metalab/templates/auth/user_list_mainpage.inc
  80. +26 −0 metalab/templates/auth/user_list_superuser.inc
  81. +99 −0 metalab/templates/base.html
  82. +23 −0 metalab/templates/cal/calendar.inc
  83. +47 −0 metalab/templates/cal/event_archive.html
  84. +53 −0 metalab/templates/cal/event_archive_year.html
  85. +7 −0 metalab/templates/cal/event_detail.html
  86. +31 −0 metalab/templates/cal/event_special_list.html
  87. +87 −0 metalab/templates/cal/eventinfo_nf.inc
  88. +1 −0 metalab/templates/cal/overview_event_navigation.inc
  89. +95 −0 metalab/templates/cellardoor.html
  90. +70 −0 metalab/templates/index.html
  91. +34 −0 metalab/templates/mahnungsmail_2009-05-27.txt
  92. +47 −0 metalab/templates/mahnungsmail_2009-05-28.txt
  93. 0 metalab/templates/members/contact_info.inc
  94. +8 −0 metalab/templates/members/member_list.html
  95. +14 −0 metalab/templates/members/member_list.inc
  96. +28 −0 metalab/templates/members/member_list_superuser.inc
  97. +22 −0 metalab/templates/members/member_login.html
  98. +25 −0 metalab/templates/members/member_update_password.html
  99. +11 −0 metalab/templates/members/member_update_password_done.html
  100. +12 −0 metalab/templates/members/member_update_userpic.html
  101. +21 −0 metalab/templates/members/members_details.html
  102. +337 −0 metalab/templates/members/members_details.inc
  103. +1 −0 metalab/templates/members/members_details_error.inc
  104. +33 −0 metalab/templates/members/members_history.html
  105. +25 −0 metalab/templates/members/members_list.html
  106. +19 −0 metalab/templates/projects/overview.inc
  107. +5 −0 metalab/templates/projects/project_archive.html
  108. +57 −0 metalab/templates/projects/projectinfo.inc
  109. +44 −0 metalab/templates/projects/projectinfo_nf.inc
  110. +21 −0 metalab/templates/registration/login.html
  111. +11 −0 metalab/templates/registration/password_change_done.html
  112. +24 −0 metalab/templates/registration/password_change_form.html
  113. +5 −0 metalab/templates/rss/change_archive.html
  114. +4 −0 metalab/templates/rss/recentchanges.inc
  115. +7 −0 metalab/templates/usbherelist/usbherelist.html
  116. +8 −0 metalab/templates/usbherelist/usbherelist.inc
  117. +14 −0 metalab/templates/welcome.mail
  118. +54 −0 metalab/templates/wikipage.html
  119. 0 projects/__init__.py
  120. +13 −0 projects/forms.py
  121. +32 −0 projects/models.py
  122. +32 −0 projects/urls.py
  123. +75 −0 projects/views.py
  124. 0 rss/__init__.py
  125. +29 −0 rss/getchanges.py
  126. +29 −0 rss/gettickets.py
  127. +11 −0 rss/models.py
  128. +25 −0 rss/urls.py
  129. +1 −0 rss/views.py
  130. 0 scrooge/__init__.py
  131. +60 −0 scrooge/models.py
  132. +11 −0 scrooge/urls.py
  133. +102 −0 scrooge/views.py
  134. +134 −0 settings.py
  135. +7 −0 templates/404.html
  136. +7 −0 templates/500.html
  137. +46 −0 templates/admin/base.html
  138. +28 −0 templates/admin_doc/index.html
  139. +32 −0 templates/announce/message_sent.html
  140. +21 −0 templates/announce/write_message.html
  141. +82 −0 templates/base.html
  142. +18 −0 templates/cal/calendar.inc
  143. +47 −0 templates/cal/event_archive.html
  144. +53 −0 templates/cal/event_archive_month.html
  145. +53 −0 templates/cal/event_archive_year.html
  146. +16 −0 templates/cal/event_detail.html
  147. +31 −0 templates/cal/event_special_list.html
  148. +82 −0 templates/cal/eventinfo_nf.inc
  149. +6 −0 templates/cal/overview_event_navigation.inc
  150. +85 −0 templates/index.html
  151. 0 templates/members/contact_info.inc
  152. +8 −0 templates/members/member_list.html
  153. +14 −0 templates/members/member_list.inc
  154. +25 −0 templates/members/member_list_superuser.inc
  155. +22 −0 templates/members/member_login.html
  156. +25 −0 templates/members/member_update_password.html
  157. +11 −0 templates/members/member_update_password_done.html
  158. +12 −0 templates/members/member_update_userpic.html
  159. +20 −0 templates/members/members_details.html
  160. +331 −0 templates/members/members_details.inc
  161. +1 −0 templates/members/members_details_error.inc
  162. +33 −0 templates/members/members_history.html
  163. +25 −0 templates/members/members_list.html
  164. +22 −0 templates/projects/overview.inc
  165. +11 −0 templates/projects/project_archive.html
  166. +40 −0 templates/projects/projectinfo.inc
  167. +44 −0 templates/projects/projectinfo_nf.inc
  168. +5 −0 templates/rss/change_archive.html
  169. +4 −0 templates/rss/recentchanges.inc
  170. +7 −0 templates/usbherelist/usbherelist.html
  171. +8 −0 templates/usbherelist/usbherelist.inc
  172. +14 −0 templates/welcome.mail
  173. +61 −0 templates/wikipage.html
  174. +32 −0 unicode.py
  175. +46 −0 urls.py
  176. 0 usbherelist/__init__.py
  177. +13 −0 usbherelist/models.py
  178. +12 −0 usbherelist/urls.py
  179. +60 −0 usbherelist/views.py
  180. 0 web/__init__.py
  181. +25 −0 web/tests.py
  182. +94 −0 web/views.py
1 .gitignore
@@ -0,0 +1 @@
+*.pyc
9 README.rst
@@ -0,0 +1,9 @@
+Metalab OS
+==========
+
+This is the django project that runs our web site at the Metalab hackerspace
+in Vienna, Austria (http://metalab.at/).
+
+The official source repository and bug tracker as of 2012-03-21 can be
+found at: https://github.com/Metalab/mos .
+
0 __init__.py
No changes.
28 admin.py
@@ -0,0 +1,28 @@
+from django.contrib import admin
+from django.contrib.auth.models import User
+
+from mos.cal.models import Event, Category, Location
+from mos.members.models import MemberAdmin
+from mos.projects.models import Project
+from mos.scrooge.models import Account, AccountAdmin, Product, ProductAdmin
+
+#models for event admin site
+calendar_admin = admin.AdminSite()
+calendar_admin.register(Event)
+calendar_admin.register(Category)
+calendar_admin.register(Location)
+
+#models for project admin site
+project_admin = admin.AdminSite()
+project_admin.register(Project)
+
+#models for user admin site
+member_admin = admin.AdminSite()
+member_admin.register(User, MemberAdmin)
+
+#models for scrooge
+scrooge_admin = admin.AdminSite()
+scrooge_admin.register(Account, AccountAdmin)
+#scrooge_admin.register(Account)
+scrooge_admin.register(Product, ProductAdmin)
+
0 announce/__init__.py
No changes.
6 announce/urls.py
@@ -0,0 +1,6 @@
+from django.conf.urls.defaults import *
+from mos import settings
+
+urlpatterns = patterns('',
+ (r'^$', 'mos.announce.views.announce'),
+)
65 announce/views.py
@@ -0,0 +1,65 @@
+#
+# Views for issueing announcements to all active members.
+#
+from django.shortcuts import render_to_response
+from django.conf import settings
+from django.contrib.auth.decorators import user_passes_test
+import django.forms as forms
+from mos.members.models import get_active_members, ContactInfo
+from django.core.mail import send_mail
+import smtplib
+from datetime import *
+
+
+class AnnouncementForm(forms.Form):
+ subject = forms.CharField(required=True, label="Thema", max_length=40)
+ body = forms.CharField(required=True, label="Mitteilung",widget=forms.Textarea,)
+ to = forms.ChoiceField(required=True, label="An", choices=(('all','all'), ('collection','collection'),))
+
+
+@user_passes_test(lambda u: (u.is_staff and u.is_authenticated()))
+def announce(request):
+ print 'wtf:' + repr(request.user)
+ form = AnnouncementForm(request.POST or None)
+ if not request.POST or not form.is_valid():
+ return render_to_response('announce/write_message.html',
+ {
+ 'form': form,
+ 'user': request.user,
+ })
+ # Valid message: send it!
+ s = ''
+
+ users = get_active_members()
+ if form.cleaned_data['to']!='all':
+ users = users.filter(paymentinfo__bank_collection_allowed=True)\
+ .filter(paymentinfo__bank_collection_mode__id=4)
+ for u in users:
+ debt = u.contactinfo_set.all()[0].get_debt_for_month(date.today())
+ if debt == 0:
+ users = users.exclude(pk=u.pk)
+
+ for user in users:
+ ci = ContactInfo.objects.get(user=user)
+ try:
+ send_mail(form.cleaned_data['subject'],
+ form.cleaned_data['body'],
+ settings.HOS_ANNOUNCE_FROM,
+ [user.email],
+ fail_silently=False)
+ ci.last_email_ok = True
+ ci.save()
+ except smtplib.SMTPException, instance:
+ f = open('/announcelog.log', 'a')
+ f.write('\n\n'+user.email)
+ f.write('\n'+repr(instance))
+ ci.last_email_ok = False
+ ci.save()
+ f.close()
+
+ return render_to_response('announce/message_sent.html',
+ {
+ 'form': form,
+ 'user': request.user,
+ 'users': users,
+ })
0 billing/__init__.py
No changes.
25 billing/models.py
@@ -0,0 +1,25 @@
+from django.db import models
+
+
+class Invoice(models.Model):
+ number = models.PositiveIntegerField(null=False, unique_for_year="date")
+ invoice_recipient = models.TextField(null=False)
+ invoice_address = models.TextField(null=False)
+ date = models.DateField(null=False)
+ invoice_mail_recipient = models.EmailField()
+ invoice_mailed_at = models.DateTimeField()
+
+ class Admin:
+ pass
+
+
+class InoviceItem(models.Model):
+ invoice = models.ForeignKey(Invoice, null=False,
+ edit_inline=models.TABULAR, core=True)
+ text = models.TextField(null=False, core=True)
+ value = models.DecimalField(null=False, decimal_places=2, max_digits=7,
+ core=True)
+ order = models.PositiveIntegerField(null=False, core=True)
+
+ class Admin:
+ pass
12 cal/__init__.py
@@ -0,0 +1,12 @@
+from icalendar import Calendar
+
+def create_calendar(components):
+ c = Calendar()
+ c.add('version', '2.0')
+ c.add('prodid', '-//Hackerspace OS//code.google.com/p/hackerspace-os//')
+ c.add('X-WR-TIMEZONE', 'Europe/Vienna')
+ c.add('X-WR-CALNAME', 'Metalab');
+ c.add('X-WR-CALDESC', 'Metalab Events Calendar');
+ for component in components:
+ c.add_component(component)
+ return c
14 cal/feeds.py
@@ -0,0 +1,14 @@
+from django.contrib.syndication.feeds import Feed
+from models import Event
+import datetime
+
+
+class EventFeed(Feed):
+ title=u'Zukuenftige Veranstaltungen'
+ link='/'
+ description=u'''zukuenftige Veranstaltungen in und um den Wiener
+ Hackerspace Metalab'''
+
+ def items(self):
+ return Event.all.filter(startDate__gte=datetime.datetime.now()).\
+ order_by('startDate')
27 cal/fields.py
@@ -0,0 +1,27 @@
+import datetime
+
+import django.forms as forms
+from django.forms import ValidationError
+
+from mos.cal.widgets import DateTimeCombiWidget
+
+
+class DateTimeCombiField(forms.MultiValueField):
+ """
+ A newform field, which provides a seperate input tag for date
+ and time, the output is merged again
+ """
+
+ def __init__(self, required=True, label=None, widget=None, initial=None):
+ fields = (forms.DateField(), forms.TimeField())
+ widget = widget or DateTimeCombiWidget()
+ super(DateTimeCombiField, self).__init__(fields, required,
+ widget, label, initial)
+
+ def compress(self, data_list):
+ if data_list:
+ try:
+ return datetime.datetime.combine(data_list[0], data_list[1])
+ except:
+ return None
+ return None
19 cal/forms.py
@@ -0,0 +1,19 @@
+from django.forms import ModelForm
+import django.forms as forms
+
+from mos.cal.fields import DateTimeCombiField
+from mos.cal.models import Event
+
+
+class EventForm(ModelForm):
+ """
+ Form to add an event
+ """
+
+ startDate = DateTimeCombiField()
+ endDate = DateTimeCombiField(required=False)
+ teaser = forms.CharField(required=False)
+
+ class Meta:
+ model = Event
+ exclude = ('where', 'created_at', 'created_by', 'deleted', 'who')
161 cal/models.py
@@ -0,0 +1,161 @@
+import datetime
+import locale
+
+from icalendar import Event as icalEvent
+from icalendar.prop import vDatetime
+
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.db import models
+from django.db.models import permalink, Q
+
+from mos.core.models import Category, Location
+from mos.cal import create_calendar
+import urllib
+
+locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')
+
+
+class EventManager(models.Manager):
+
+ def get_query_set(self):
+ return super(EventManager, self).get_query_set().filter(deleted=False)
+
+
+class FutureEventFixedNumberManager(EventManager):
+
+ def get_query_set(self):
+ """
+ Get <num> future events, or if there aren't enough,
+ get <num> latest+future events.
+ """
+
+ DEFAULT_NUM = 5
+ if(hasattr(settings, 'HOS_HOME_EVENT_NUM')):
+ num = settings.HOS_HOME_EVENT_NUM
+ else:
+ num = DEFAULT_NUM
+
+ return self.get_n(num)
+
+ def get_n(self, num):
+
+ all = super(FutureEventFixedNumberManager, self).get_query_set().\
+ order_by('startDate')
+
+ if num==0:
+ return all
+
+ future = all.filter((Q(endDate__gte=datetime.datetime.now())) |
+ (Q(endDate__isnull=True) &
+ Q(startDate__gte=datetime.datetime.now()-\
+ datetime.timedelta(hours=5))))\
+ .order_by('startDate') # event visible 5 hours
+ # after it started
+
+ if(future.count()<num):
+ if(all.count()-num>=0):
+ latest = all[all.count()-num:all.count()]
+ else:
+ latest = all
+ else:
+ latest = future[:num]
+
+ return latest
+
+
+class Event(models.Model):
+ """
+ Represents an event
+ """
+
+ name = models.CharField(max_length=200)
+ teaser = models.TextField(max_length=200, blank=True, null=True)
+ wikiPage = models.CharField(max_length=200)
+
+ startDate = models.DateTimeField()
+ endDate = models.DateTimeField(blank=True, null=True)
+
+ who = models.CharField(max_length=200, blank=True)
+ where = models.CharField(max_length=200, blank=True)
+
+ created_at = models.DateTimeField(default=datetime.datetime.now())
+ created_by = models.ForeignKey(User)
+
+ deleted = models.BooleanField(default=False)
+
+ category = models.ForeignKey(Category, blank=True, null=True)
+ location = models.ForeignKey(Location, blank=True, null=True)
+
+ objects = models.Manager()
+ all = EventManager()
+ future = FutureEventFixedNumberManager()
+
+ def __unicode__(self):
+ status = ''
+ if self.deleted:
+ status = ' [deleted]'
+ return u'%s (%s)%s' % (self.name, self.startDate, status)
+
+ def past(self):
+ return self.startDate < datetime.datetime.now()
+
+ @permalink
+ def get_absolute_url(self):
+ return ('cal_event_detail', (self.id,),)
+
+ def save(self, editor=False, new=False):
+ if new and editor != False:
+ self.created_by = editor
+ self.created_by.save()
+
+ super(Event, self).save()
+
+ def start_end_date_eq(self):
+ return self.startDate.date() == self.endDate.date()
+
+ def delete(self):
+ self.deleted = True
+
+
+
+ def get_icalendar_event(self):
+ domain = Site.objects.get_current().domain
+ rv = icalEvent()
+
+ rv.add('uid', u'%d@%s' % (self.id, domain))
+
+ rv.add('summary', unicode(self.name))
+ rv.add('dtstart', vDatetime(self.startDate).ical(), encode=0)
+ rv.add('dtstamp', vDatetime(self.created_at).ical(), encode=0)
+ rv.add('url', urllib.quote((u'http://%s/wiki/%s' % (domain, unicode(self.wikiPage))).encode('utf-8')) )
+
+ if self.teaser:
+ rv.add('description', unicode(self.teaser))
+
+ if self.endDate:
+ rv.add('dtend', vDatetime(self.endDate).ical(), encode=0)
+
+ if self.who:
+ rv.add('organizer', unicode(self.who))
+ elif self.created_by_id and User.objects.filter(id=self.created_by_id):
+ rv.add('organizer', unicode(self.created_by))
+
+ if self.location:
+ rv.add('location', u'Metalab ' + unicode(self.location))
+ elif self.where:
+ rv.add('location', u'Metalab ' + unicode(self.where))
+
+ if self.category:
+ rv.add('categories', unicode(self.category))
+
+ return rv
+
+ def get_icalendar(self):
+ return create_calendar([self.get_icalendar_event()])
+
+ @permalink
+ def get_icalendar_url(self):
+ return ( 'cal_event_icalendar', (self.id,) )
+
0 cal/templatetags/__init__.py
No changes.
33 cal/templatetags/cal_tags.py
@@ -0,0 +1,33 @@
+from django import template
+from django.template import Variable
+from django.core.exceptions import ObjectDoesNotExist
+from django.contrib.auth.models import User
+
+from mos.cal.models import Event
+from mos.core.models import Location, Category
+
+
+register = template.Library()
+
+
+@register.tag(name="events_by_type")
+def do_events_by_type(parser, token):
+ try:
+ # split_contents() knows not to split quoted strings.
+ tag_name, name = token.split_contents()
+ except ValueError:
+ raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0]
+ return EventsByTypeNode(name)
+
+
+class EventsByTypeNode(template.Node):
+ def __init__(self, name):
+ self.obj = Variable(name)
+
+ def render(self, context):
+ kw = self.obj.resolve(context).__class__.__name__.lower() + '__name'
+ filter_arg = {str(kw): self.obj.resolve(context).name}
+ obj_sub_list = Event.objects.filter(**filter_arg)
+ context['latestevents'] = obj_sub_list
+ return ''
+
90 cal/tests.py
@@ -0,0 +1,90 @@
+from django.contrib.auth.models import User
+from django.test import TestCase
+from django.test.client import Client
+
+from mos.cal.forms import EventForm
+from mos.cal.models import Event
+
+
+correct_data = {'name': 'TestEvent1',
+ 'teaser': 'Event des Jahres',
+ 'wikiPage': '/wiki/foo',
+ 'startDate_0': '2008-06-06',
+ 'startDate_1': '15:00',
+ 'endDate_0': '2009-04-02',
+ 'endDate_1': '14:00'}
+
+
+class EventFormTest(TestCase):
+ fixtures = ['initial_user.json']
+
+ def setUp(self):
+ self.wrong_data = correct_data.copy()
+
+ #set error conditions
+ self.wrong_data['name'] = ''
+ self.wrong_data['startDate_0'] = 'asdasd'
+ self.wrong_data['endDate_1'] = 'foop'
+ self.wrong_data['wikiPage'] = ''
+
+ def testFormCorrectData(self):
+ """Creates a event form with valid information"""
+
+ f = EventForm(correct_data)
+ assert f.is_valid() is True, 'Correct set of event data but form\
+ errors'
+
+ def testFormWrongData(self):
+ """Creates a event form with invalid information"""
+
+ f = EventForm(self.wrong_data)
+ assert f.is_valid() is False, 'Name of the event is missing, but no\
+ error raised'
+ assert f.errors['name'] != "", 'Name of the event is missing, but no \
+ error raised'
+ assert f.errors['startDate'] != "", 'Wrong date, but no error raised'
+ assert f.errors['endDate'] != "", 'Wrong time, but no error raised'
+ assert f.errors['wikiPage'] != "", 'Wikipage missing, but no error \
+ raised'
+ print f.errors
+
+ def testSaveFromForm(self):
+ """Adds a user with valid information"""
+
+ f = EventForm(correct_data)
+ if f.is_valid():
+ event_data = f.save(commit=False)
+ user = User.objects.get(username='d3f3nd3r')
+ event_data.save(editor=user, new=True)
+
+ assert Event.objects.get(name='TestEvent1'), 'couldnt add event'
+
+
+class EventViewsTest(TestCase):
+ fixtures = ['initial_user.json']
+
+ def setUp(self):
+ self.c = Client()
+ self.c.login(username='d3f3nd3r', password='d3f3nd3r')
+
+ self.minimal_data_set = correct_data.copy()
+ self.minimal_data_set['endDate_0'] = ''
+ self.minimal_data_set['endDate_1'] = ''
+ self.minimal_data_set['teaser'] = ''
+
+ def testAddNewEvent(self):
+ """ Adds a new event via the view """
+
+ response = self.c.post('/cal/new/', # adds a new event via
+ correct_data) # the view update_event
+ self.assertContains(response, 'TestEvent1', count=None,
+ status_code=200)
+ self.assertContains(response, '06.06.2008 15:00', count=None,
+ status_code=200)
+
+ response = self.c.post('/cal/new/', # adds a new event via
+ self.minimal_data_set) # the view update_event
+ self.assertContains(response, 'TestEvent1', count=None,
+ status_code=200)
+ self.assertContains(response, '06.06.2008 15:00', count=None,
+ status_code=200)
75 cal/urls.py
@@ -0,0 +1,75 @@
+from django.conf.urls.defaults import *
+
+from mos.cal.models import Event, Location, Category
+
+
+date_dict = {
+ 'queryset': Event.all.all(),
+ 'date_field': 'startDate',
+ 'allow_future': True,
+}
+
+archive_year_dict = date_dict.copy()
+archive_year_dict.update(make_object_list=True)
+
+archive_index_dict = date_dict.copy()
+archive_index_dict.update(
+ num_latest=100,
+ template_object_name='latestevents',
+ allow_empty=True,
+ )
+
+event_detail_dict = date_dict.copy()
+event_detail_dict.update(template_object_name='event')
+
+info_dict = {
+ 'queryset': Event.all.all(),
+ 'template_object_name': 'event',
+}
+
+info_dict_locations = {
+ 'queryset': Location.objects.all(),
+ # 'template_object_name': 'locations',
+ 'template_name': 'cal/event_special_list.html',
+ 'extra_context': {'events_by': ' Locations'}
+}
+
+info_dict_categories = {
+ 'queryset': Category.objects.all(),
+ # 'template_object_name': 'locations',
+ 'template_name': 'cal/event_special_list.html',
+ 'extra_context': {'events_by': ' Categories'}
+}
+
+
+urlpatterns = patterns('django.views.generic.date_based',
+ (r'^(?P<year>\d{4})/$',
+ 'archive_year', archive_year_dict, 'cal_archive_year'),
+)
+
+urlpatterns += patterns('',
+ (r'^$',
+ 'mos.cal.views.index', {}, 'cal_index'),
+ (r'^(?P<year>\d{4})/(?P<month>\d{2})/$',
+ 'mos.cal.views.monthly',),
+ (r'^special/(?P<typ>\w+)/(?P<name>\w+)/$',
+ 'mos.cal.views.display_special_events'),
+ (r'^event/(?P<object_id>\d+)/$',
+ 'django.views.generic.list_detail.object_detail', info_dict, 'cal_event_detail'),
+ (r'^event/(?P<object_id>\d+)/update/$',
+ 'mos.cal.views.update_event', {'new': False}),
+ (r'^(?P<object_id>\d+)/update/$',
+ 'mos.cal.views.update_event', {'new': False}),
+ (r'^event/(?P<object_id>\d+)/delete/', 'mos.cal.views.delete_event'),
+ (r'^(?P<object_id>\d+)/delete/', 'mos.cal.views.delete_event'),
+ (r'^event/(?P<object_id>\d+)/icalendar/', 'mos.cal.views.event_icalendar', {},
+ 'cal_event_icalendar'),
+ (r'^export/ical/$', 'mos.cal.views.complete_ical', {}, 'full_ical'),
+ (r'^event/new/$', 'mos.cal.views.update_event', {'new': True}),
+ (r'^new/$', 'mos.cal.views.update_event', {'new': True}),
+ (r'^locations/$',
+ 'django.views.generic.list_detail.object_list', info_dict_locations),
+ (r'^categories/$',
+ 'django.views.generic.list_detail.object_list', info_dict_categories),
+ (r'^ajax/list/(?P<number>\d*)/?$', 'mos.cal.views.event_list'),
+)
203 cal/views.py
@@ -0,0 +1,203 @@
+# Create your views here.
+from dateutil.parser import *
+
+from icalendar import Calendar as icalCalendar
+
+from django.contrib.auth.decorators import login_required
+from django.shortcuts import render_to_response, get_object_or_404
+from django.template import RequestContext
+from django.core.exceptions import ObjectDoesNotExist
+from django.http import HttpResponse, HttpResponseRedirect
+
+from mos.cal.forms import EventForm
+from mos.cal.models import Event, Category, Location
+from mos.cal import create_calendar
+
+from dateutil import relativedelta
+
+from calendar import HTMLCalendar
+from datetime import date
+from itertools import groupby
+
+from django.utils.html import conditional_escape as esc
+from django.utils.safestring import mark_safe
+
+class EventCalendar(HTMLCalendar):
+
+ def __init__(self, events, admin=False):
+ super(EventCalendar, self).__init__()
+ self.events = events
+ self.admin = admin
+
+ def formatday(self, day, weekday):
+ if day != 0:
+ d = date(self.year, self.month, day)
+ d1 = d+relativedelta.relativedelta(days=1) # next day
+ cssclass = self.cssclasses[weekday]
+ if date.today() == date(self.year, self.month, day):
+ cssclass += ' today'
+ cssclass += ' filled'
+ body = ['<ul>']
+ for event in self.events.exclude(startDate__gt=d1).exclude(endDate__lt=d,endDate__isnull=False).exclude(endDate__isnull=True, startDate__lt=d):
+ body.append('<li>')
+ body.append('<a href="/wiki/%s">' % event.wikiPage)
+ body.append(event.startDate.strftime('%H:%M') + ' ' + esc(event.name))
+ body.append('</a>')
+ if self.admin:
+ body.append(u'<a href="%s" class="edit">/e</a>' % event.get_absolute_url())
+ body.append('</li>')
+ body.append('</ul>')
+ return self.day_cell(cssclass, '%d %s' % (day, (u''.join(body)).encode('utf-8')))
+ return self.day_cell(cssclass, day)
+ return self.day_cell('noday', '&nbsp;')
+
+ def formatmonth(self, year, month, withyear=False):
+ self.year, self.month = year, month
+
+ d = date(int(year), int(month), 1)
+ prev = d - relativedelta.relativedelta(months=1)
+ next = d + relativedelta.relativedelta(months=1)
+ head = u'<a href="/calendar/%04d/%02d/">&lt;</a> <a href="/calendar/%04d/%02d/">&gt;</a>'% (prev.year, prev.month, next.year, next.month)
+ return head.encode('utf-8')+super(EventCalendar, self).formatmonth(year, month, withyear)
+
+ def group_by_day(self, events):
+ field = lambda event: event.startDate.day
+ return dict(
+ [(day, list(items)) for day, items in groupby(events, field)]
+ )
+
+ def day_cell(self, cssclass, body):
+ return '<td class="%s">%s</td>' % (cssclass, body)
+
+ def currentmonth(self):
+ t = date.today()
+ return self.formatmonth(t.year, t.month)
+
+def index(request):
+ d = date.today()-relativedelta.relativedelta(days=2)
+ cal = EventCalendar(Event.all, request.user.is_authenticated()).currentmonth()
+ date_list = Event.all.all().dates('startDate','year')
+ latest_events = Event.all.filter(startDate__gte=d).order_by('startDate')
+
+ return render_to_response('cal/event_archive.html', {'rendered_calendar': mark_safe(cal),'date_list': date_list, 'latestevents': latest_events}, RequestContext(request))
+
+def monthly(request, year, month):
+ s = date(int(year), int(month), 1)
+ e = date(int(year), int(month), 1) + relativedelta.relativedelta(months=1)
+ latest_events = Event.all.filter(startDate__gte=s, startDate__lt=e).order_by( 'startDate')
+ cal = EventCalendar(Event.all, request.user.is_authenticated()).formatmonth(int(year), int(month))
+ date_list = Event.all.all().dates('startDate','year')
+
+ return render_to_response('cal/event_archive.html', {'rendered_calendar': mark_safe(cal),'date_list': date_list, 'latestevents': latest_events,}, RequestContext(request))
+
+def display_special_events(request, typ, name):
+ """
+ Displays special events by location or category
+ """
+
+ try:
+ if typ == 'Category':
+ events = Event.objects.filter(category__name=name)
+ des = get_object_or_404(Category, name=name)
+ elif typ == 'Location':
+ events = Event.objects.filter(location__name=name)
+ des = get_object_or_404(Location, name=name)
+ else:
+ events = None
+ des = None
+
+ except ObjectDoesNotExist:
+ events = None
+
+ return render_to_response('cal/event_archive.html', {
+ 'latestevents': events,
+ 'title': name,
+ 'type': typ,
+ 'description': des,
+ }, context_instance=RequestContext(request))
+
+
+@login_required
+def delete_event(request, object_id=None):
+ if not request.method == 'POST' or not request.user.is_authenticated():
+ return
+
+ event = Event.all.get(id=object_id)
+
+ event.delete()
+ event.save()
+
+ return HttpResponse()
+
+
+@login_required
+def update_event(request, new, object_id=None):
+ if not request.POST or not request.user.is_authenticated():
+ #return
+ pass
+
+ if not new:
+ event = Event.all.get(id=object_id)
+ else:
+ event = Event()
+
+ event_error_id = None
+
+ event_valid = True
+
+ if request.method == 'POST':
+ event_form = EventForm(request.POST, instance=event)
+
+ if event_form.is_valid():
+ event_data = event_form.save(commit=False)
+ event_data.save(request.user, new)
+ event = Event.objects.get(id=event_data.id)
+ else:
+ event_valid = False
+ event_error_id = event.id
+
+ else:
+ event_form = EventForm()
+
+ response = render_to_response('cal/eventinfo_nf.inc', {
+ 'event_has_error': not event_valid,
+ 'event_form': event_form,
+ 'event': event,
+ 'new': not event.pk,
+ }, context_instance=RequestContext(request))
+
+ if not event_valid:
+ response.status_code = 500
+
+ return response
+
+
+def event_list(request, number=0):
+ events = Event.future.get_n(long(number) if number != '' else 0)
+
+ if not number:
+ events = events.reverse()
+
+ return render_to_response('cal/calendar.inc',
+ {'latestevents': events},
+ context_instance=RequestContext(request))
+
+def event_icalendar(request, object_id):
+ event = get_object_or_404(Event, pk=object_id)
+
+ response = HttpResponse(event.get_icalendar().as_string(),
+ mimetype='text/calendar; charset=utf-8')
+
+ response['Content-Disposition'] = (u'filename="' + unicode(event.startDate.strftime('%Y-%m-%d')) + u' - ' + unicode(event.name) + u'.ics"').encode('ascii','ignore')
+
+ return response
+
+def complete_ical(request, number=0):
+ events = Event.future.get_n(long(number) if number != '' else 0)
+
+ if not number:
+ events = events.reverse()
+
+ calendar = create_calendar([x.get_icalendar_event() for x in events])
+ return HttpResponse(calendar.as_string(), mimetype='text/calendar; charset=utf-8')
+
19 cal/widgets.py
@@ -0,0 +1,19 @@
+import django.forms as forms
+
+
+class DateTimeCombiWidget(forms.MultiWidget):
+ """
+ A newform widget, which provides a seperate input tag for
+ date and time, the output is merged again
+ """
+
+ def __init__(self, attrs=None):
+ widgets = (forms.TextInput(attrs={'class': 'vDateField'}),
+ forms.TextInput(attrs={'class': 'vTimeField'}))
+ super(DateTimeCombiWidget, self).__init__(widgets, attrs)
+
+ def decompress(self, value):
+
+ if value:
+ return [value.date(), value.time()]
+ return ['', '']
0 core/__init__.py
No changes.
36 core/context_processors.py
@@ -0,0 +1,36 @@
+from django.conf import settings
+
+
+def custom_settings_global(request):
+ """
+ sets custom style and name as global template variables
+ """
+
+ return {'custom_style': settings.HOS_CUSTOM_STYLE,
+ 'HOS_NAME': settings.HOS_NAME,
+ }
+
+
+def custom_settings_wiki(request):
+ """
+ Sets the custom wiki url
+ """
+
+ return {'HOS_WIKI_URL': settings.HOS_WIKI_URL}
+
+
+def custom_settings_main(request):
+ """
+ sets customizations specified in settings.py for the main page
+ """
+
+ return {'introduction_text': settings.HOS_INTRODUCTION,
+ 'members': settings.HOS_MEMBER_GALLERY,
+ 'gallery': settings.HOS_LOCATION_GALLERY,
+ 'openlab': settings.HOS_OPENLAB,
+ 'calendar': settings.HOS_CALENDAR,
+ 'projects': settings.HOS_PROJECTS,
+ 'recent_changes': settings.HOS_RECENT_CHANGES,
+ 'metasense': settings.HOS_METASENSE,
+ 'HOS_WIKI_CHANGE_URL': settings.HOS_WIKI_CHANGE_URL,
+ }
46 core/middleware.py
@@ -0,0 +1,46 @@
+"""
+django_playground.core.middleware
+inspired by www.pylucid.org
+see http://trac.pylucid.net/browser/trunk/pylucid/PyLucid/middlewares/\
+pagestats.py for authors
+"""
+import time
+
+from django.db import connection
+from django.utils.encoding import force_unicode
+from mos.core.utils import human_readable_time
+
+
+TAG = '<!-- footer_stats -->'
+FOOTER_STAT_STRING = 'renderd in %(render_time)s - %(queries)s sql queries'
+
+
+class SetStatFooter:
+ """
+ Sets some performance data (number of queries,..
+ """
+
+ def process_request(self, request):
+ self.time_started = time.time()
+ self.old_queries = len(connection.queries)
+
+ def process_response(self, request, response):
+ try:
+ if 'text/html' not in response['Content-Type']:
+ return response
+ if request.is_ajax():
+ return response
+ if response.status_code != 200:
+ return response
+
+ queries = len(connection.queries) - self.old_queries
+
+ render_time = human_readable_time(time.time() - self.time_started)
+ stats = FOOTER_STAT_STRING % {'render_time': render_time,
+ 'queries': queries}
+ content = response.content
+ response.content = force_unicode(content).replace(TAG, stats)
+ except:
+ pass
+
+ return response
29 core/models.py
@@ -0,0 +1,29 @@
+from django.db import models
+
+
+class Category(models.Model):
+ """
+ represents a Category (name, description)
+ used in cal.models.Event
+ """
+
+ name = models.CharField(max_length=30)
+ description = models.CharField(max_length=200)
+
+ def __str__(self):
+ return u"%s" % self.name
+
+
+class Location(models.Model):
+ """
+ represents a location(name, description, country)
+ used in cal.models.Event
+ """
+
+ name = models.CharField(max_length=30)
+ description = models.CharField(max_length=200)
+
+ country = models.CharField(max_length=100)
+
+ def __str__(self):
+ return u"%s" % self.name
0 core/templatetags/__init__.py
No changes.
42 core/templatetags/makeform.py
@@ -0,0 +1,42 @@
+import sys
+from django import template
+
+register = template.Library()
+
+@register.tag(name="makeform")
+def do_makeform(parser, token):
+ """ do_makeform variable path.to.form.class target_name """
+ try:
+ tag_name, context_var, form_path, target_name = token.split_contents()
+ except ValueError:
+ raise template.TemplateSyntaxError, "%r tag requires two arguments" % token.contents.split()[0]
+
+ try:
+ modulename, classname = form_path.rsplit('.',1)
+ module = __import__(modulename, fromlist=[classname])
+ except:
+ raise template.TemplateSyntaxError, "the path to form class (%s) should be import-able! %r" % (form_path.rsplit('.', 1)[0], token.contents.split()[0])
+
+ cls = getattr(module, classname)
+
+ return MakeFormNode(context_var, cls, target_name)
+
+class MakeFormNode(template.Node):
+ def __init__(self, context_var, cls, target_name):
+ self.context_var = template.Variable(context_var) if context_var != 'None' else None
+ self.cls = cls
+ self.target_name = target_name
+
+ def render(self, context):
+ if self.context_var:
+ try:
+ actual_var = self.context_var.resolve(context)
+ except template.VariableDoesNotExist:
+ raise template.TemplateSyntaxError, 'MakeFormNode cannot resolve variable'
+
+ if not self.context_var:
+ context[self.target_name]=self.cls()
+ else:
+ context[self.target_name]=self.cls(instance=actual_var)
+
+ return ''
19 core/utils.py
@@ -0,0 +1,19 @@
+"""
+used from pylucid (www.pylucid.org)
+see http://trac.pylucid.net/browser/trunk/pylucid/PyLucid/template_addons/\
+filters.py for author
+information
+"""
+
+import time
+
+
+def human_readable_time(t):
+ """ converts (milli-)seconds into a nice string """
+
+ if t<1:
+ return ("%.1f ms") % (t * 1000)
+ elif t>60:
+ return ("%.1f min") % (t/60.0)
+ else:
+ return ("%.1f sec") % t
15 domail.py
@@ -0,0 +1,15 @@
+import os
+import sys
+
+from mos.members.models import get_active_members
+
+
+os.environ['DJANGO_SETTINGS_MODULE'] = "mos.settings"
+sys.path.append("/django/deployment")
+
+
+for user in get_active_members():
+ user.is_active = True
+ user.save()
+
+ print user, user.email
27 import_payment.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+import os,sys
+
+
+_DIRNAME = os.path.dirname(globals()["__file__"])
+sys.path.append(os.path.join(_DIRNAME, '..'))
+sys.path.append('/django/svn/trunk')
+from django.core.management import setup_environ
+try:
+ import settings # Assumed to be in the same directory.
+except ImportError:
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the \
+ directory containing %r. It appears you've \
+ customized things.\nYou'll have to run django-admin.py,\
+ passing it your settings module.\n(If the file \
+ settings.py does indeed exist, it's causing an \
+ ImportError somehow.)\n" % __file__)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ setup_environ(settings)
+ from members.models import *
+ if len(sys.argv) < 3:
+ print "%s <filename> <yyyy-MM-dd>" % sys.argv[0]
+ sys.exit(1)
+
+ Payment.objects.import_smallfile(sys.argv[1], sys.argv[2])
26 list_intern_emails.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+import sys
+import os
+
+_DIRNAME = os.path.dirname(globals()["__file__"])
+sys.path.append(os.path.join(_DIRNAME, '..'))
+sys.path.append('/django/svn/trunk')
+from django.core.management import setup_environ
+try:
+ import settings # Assumed to be in the same directory.
+except ImportError:
+ import sys
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the \
+ directory containing %r. It appears you've \
+ customized things.\nYou'll have to run django-admin.py,\
+ passing it your settings module.\n(If the file \
+ settings.py does indeed exist, it's causing an \
+ ImportError somehow.)\n" % __file__)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ setup_environ(settings)
+ from members.models import *
+ members_on_intern = get_mailinglist_members().filter(contactinfo__on_intern_list=True)
+ addresses = [x.contactinfo_set.all()[0].intern_list_email for x in members_on_intern if x.contactinfo_set.all()[0].intern_list_email != '']
+ sys.stdout.write('\n'.join(addresses))
26 list_intern_emails_new.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+import sys
+import os
+
+_DIRNAME = os.path.dirname(globals()["__file__"])
+sys.path.append(os.path.join(_DIRNAME, '..'))
+sys.path.append('/django/svn/trunk')
+from django.core.management import setup_environ
+try:
+ import settings # Assumed to be in the same directory.
+except ImportError:
+ import sys
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the \
+ directory containing %r. It appears you've \
+ customized things.\nYou'll have to run django-admin.py,\
+ passing it your settings module.\n(If the file \
+ settings.py does indeed exist, it's causing an \
+ ImportError somehow.)\n" % __file__)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ setup_environ(settings)
+ from members.models import *
+ members_on_intern = get_mailinglist_members().filter(contactinfo__on_intern_list=True)
+ addresses = [x.contactinfo_set.all()[0].intern_list_email for x in members_on_intern if x.contactinfo_set.all()[0].intern_list_email != '']
+ sys.stdout.write('\n'.join(addresses))
22 manage.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+import sys
+import os
+
+_DIRNAME = os.path.dirname(globals()["__file__"])
+sys.path.append(os.path.join(_DIRNAME, '..'))
+sys.path.append('/django/svn/trunk')
+from django.core.management import execute_manager
+try:
+ import settings # Assumed to be in the same directory.
+except ImportError:
+ import sys
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the \
+ directory containing %r. It appears you've \
+ customized things.\nYou'll have to run django-admin.py,\
+ passing it your settings module.\n(If the file \
+ settings.py does indeed exist, it's causing an \
+ ImportError somehow.)\n" % __file__)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ execute_manager(settings)
0 members/__init__.py
No changes.
41 members/forms.py
@@ -0,0 +1,41 @@
+import django.forms as forms
+
+from django.contrib.auth.models import User
+from django.forms.models import ModelForm
+
+from mos.members.models import ContactInfo
+
+
+class UserNameForm(ModelForm):
+
+ class Meta:
+ model = User
+ fields = ('first_name', 'last_name')
+
+class UserInternListForm(ModelForm):
+
+ class Meta:
+ model = ContactInfo
+ fields = ('on_intern_list', 'intern_list_email')
+
+
+class UserEmailForm(ModelForm):
+ email = forms.EmailField(required=True)
+
+ class Meta:
+ model = User
+ fields = ('email')
+
+
+class UserAdressForm(ModelForm):
+
+ class Meta:
+ model = ContactInfo
+ fields = ('street', 'city', 'postcode', 'country')
+
+
+class UserImageForm(ModelForm):
+
+ class Meta:
+ model = ContactInfo
+ fields = ('image')
0 members/management/__init__.py
No changes.
0 members/management/commands/__init__.py
No changes.
33 members/management/commands/import_payment.py
@@ -0,0 +1,33 @@
+from django.core.management.base import BaseCommand
+from django.core.management.color import no_style
+from optparse import make_option
+import sys
+import os
+
+try:
+ set
+except NameError:
+ from sets import Set as set # Python 2.3 fallback
+
+class Command(BaseCommand):
+ help = 'Load months collection CVS'
+ args = "./manage.py import_payment absolute_filepath date(yyyy-mm-dd)"
+
+ def handle(self, *args, **kwargs):
+ from django.db.models import get_apps
+ from django.core import serializers
+ from django.db import connection, transaction
+ from django.conf import settings
+ from mos.members.models import Payment
+
+ if len(args)!=2:
+ print self.help
+ print self.args
+ return
+
+ file = args[0]
+ date = args[1]
+
+ print 'importing'
+ Payment.objects.import_smallfile(file, date)
+ print 'done'
412 members/models.py
@@ -0,0 +1,412 @@
+#!/usr/bin/python
+# vim: set fileencoding=utf-8
+
+import datetime
+
+from django.db import models
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.db.models import Q
+from django.core.exceptions import ObjectDoesNotExist
+from django.contrib import admin
+from django.utils.encoding import smart_unicode
+
+
+
+class PaymentInfo(models.Model):
+ bank_collection_allowed = models.BooleanField()
+ bank_collection_mode = models.ForeignKey('BankCollectionMode')
+ bank_account_owner = models.CharField(max_length=200, blank=True)
+ bank_account_number = models.CharField(max_length=20, blank=True)
+ bank_name = models.CharField(max_length=100, blank=True)
+ bank_code = models.CharField(max_length=20, blank=True)
+
+ user = models.ForeignKey(User, unique=True)
+
+
+class ContactInfo(models.Model):
+
+ def get_image_path(self, filename):
+ name, ext = filename.rsplit('.', 1)
+ return 'userpics/%s.%s' %(self.user.username, ext)
+
+ on_intern_list = models.BooleanField(default=True)
+ intern_list_email = models.EmailField(blank=True)
+
+ street = models.CharField(max_length=200)
+ postcode = models.CharField(max_length=10)
+ city = models.CharField(max_length=100)
+ country = models.CharField(max_length=100)
+
+ phone_number = models.CharField(max_length=32, blank=True)
+ birthday = models.DateField(null=True, blank=True)
+
+ wiki_name = models.CharField(max_length=50, blank=True, null=True)
+ image = models.ImageField(upload_to=get_image_path, blank=True)
+
+ user = models.ForeignKey(User, unique=True)
+
+ last_email_ok = models.BooleanField(null=True)
+ has_active_key = models.BooleanField(null=False)
+ key_id = models.CharField(max_length=100, blank=True, null=True)
+
+
+ remark = models.TextField(null=True, blank=True)
+
+
+ def get_debts(self):
+ #FIXME: this is broken because it assumes that a membership period
+ # has a constant fee
+ arrears = 0
+ mp_list = MembershipPeriod.objects.filter(user=self.user)
+ for mp in mp_list:
+ fee = MembershipFee.objects.get(kind_of_membership=\
+ mp.kind_of_membership)
+ if fee.amount > 0:
+ arrears += mp.get_duration_in_month()*fee.amount
+ return arrears - self.get_all_payments()
+
+ def get_debt_for_month(self, date_in_month):
+ #see if the there is a membership period for the month
+ mp_list = MembershipPeriod.objects.filter(user=self.user).filter(
+ Q(begin__lte=date_in_month),
+ Q(end__isnull=True) | Q(end__gte=date_in_month))
+
+ if mp_list.count() == 0:
+ return 0
+ else:
+ #find the membership fee for the month and kind
+ #of membership and return amount
+ mp = mp_list[0]
+ fee = mp.kind_of_membership.membershipfee_set.filter(
+ Q(start__lte=date_in_month),
+ Q(end__isnull=True) | Q(end__gte=date_in_month))[0]
+
+ return fee.amount
+
+ def get_all_payments(self):
+ payments = 0
+ p_list = Payment.objects.filter(user=self.user)
+ for p in p_list:
+ payments += p.amount
+ return payments
+
+ def get_date_of_entry(self):
+ # FIXME: the order here is wrong, didn't change it since i don't have time to check all implications
+ # sf - 2010 07 27
+ mp = MembershipPeriod.objects.filter(user=self.user)\
+ .order_by('-begin')[0]
+ return mp.begin
+
+ def get_current_membership_period(self):
+ # FIXME: the order here is wrong, didn't change it since i don't have time to check all implications
+ # sf - 2010 07 27
+ mp = MembershipPeriod.objects.filter(user=self.user)\
+ .order_by('begin')[0]
+ if mp.end is None:
+ return mp
+ else:
+ return None
+ return mp.begin
+
+ def is_active_key_member(self):
+ # FIXME: the order here is wrong, didn't change it since i don't have time to check all implications
+ # sf - 2010 07 27
+ mp = MembershipPeriod.objects.filter(user=self.user)\
+ .order_by('-begin')[0]
+ if not mp.end is None:
+ return False
+
+ return not self.key_id is None and self.has_active_key
+
+ def get_wikilink(self):
+ wikiname = self.wiki_name
+ if not wikiname:
+ wikiname = self.user.username
+
+ return u'%sBenutzer:%s' % (settings.HOS_WIKI_URL, wikiname)
+
+
+def get_active_members():
+ return User.objects.filter(
+ Q(membershipperiod__begin__lte=datetime.datetime.now()),
+ Q(membershipperiod__end__isnull=True) |
+ Q(membershipperiod__end__gte=datetime.datetime.now()))\
+ .distinct()
+
+def get_mailinglist_members():
+ return User.objects.filter(
+ Q(membershipperiod__end__isnull=True) |
+ Q(membershipperiod__end__gte=datetime.datetime.now()))\
+ .distinct()
+
+def get_active_members_for(dt):
+ return User.objects.filter(
+ Q(membershipperiod__begin__lte=dt),
+ Q(membershipperiod__end__isnull=True) |
+ Q(membershipperiod__end__gte=dt))\
+ .distinct()
+
+def get_active_and_future_members():
+ return User.objects.filter(
+ Q(membershipperiod__end__isnull=True) |
+ Q(membershipperiod__end__gte=datetime.datetime.now()))\
+ .distinct()
+
+def get_active_membership_months_until(date):
+ periods = MembershipPeriod.objects.filter(Q(begin__lte=date))
+ res = {}
+ for p in periods:
+ begin = get_months(p.begin)
+ end = get_months(date if p.end is None or p.end > date else p.end)
+ nrMonths = end - begin + 1
+ kind = p.kind_of_membership.name
+ if res.has_key(kind):
+ res[kind] += nrMonths
+ else:
+ res[kind] = nrMonths
+
+ return res
+
+def get_months(date):
+ return date.month + 12*date.year
+
+class BankCollectionMode(models.Model):
+ name = models.CharField(max_length=20)
+ num_month = models.IntegerField()
+
+ def __unicode__(self):
+ return u"%s" % self.name
+
+
+class MembershipPeriod(models.Model):
+ begin = models.DateField()
+ end = models.DateField(null=True, blank=True)
+ user = models.ForeignKey(User)
+ kind_of_membership = models.ForeignKey('KindOfMembership')
+
+ def __unicode__(self):
+ return u"%s" % self.user.username
+
+ def get_duration_in_month(self):
+ if self.end is None:
+ end = datetime.date.today()
+ else:
+ end = self.end
+ if end < self.begin:
+ return 0
+
+ begin = datetime.date(self.begin.year, self.begin.month, 1)
+ end = datetime.date(end.year, end.month, 2)
+
+ month = 0
+ while begin < end:
+ if begin.month == 12:
+ begin = datetime.date(begin.year + 1, 1, 1)
+ else:
+ begin = datetime.date(begin.year, begin.month + 1, 1)
+ month += 1
+ return month
+
+
+class MembershipFee(models.Model):
+ """
+ Defines the membership fee for a certain period of time.
+ With this class it is possible to define different amount of
+ membership fees for different periods of time and for different
+ kind of members, e.g. pupils, unemployees, normal members, ...
+ """
+
+ kind_of_membership = models.ForeignKey('KindOfMembership')
+ start = models.DateField()
+ end = models.DateField(null=True, blank=True)
+ amount = models.IntegerField()
+
+ def __unicode__(self):
+ return u"%s - %d" % (self.kind_of_membership, self.amount)
+
+
+class KindOfMembership(models.Model):
+ name = models.CharField(max_length=30)
+
+ def __unicode__(self):
+ return u"%s" % self.name
+
+
+class PaymentManager(models.Manager):
+ def import_smallfile(self, filename, date):
+ import csv
+
+ f = open(filename, 'r')
+ r = csv.reader(f, delimiter=";")
+
+ for line in r:
+ if len(line)<2:
+ print line
+ continue
+ try:
+ u = User.objects.get(first_name=smart_unicode(line[0]), last_name=smart_unicode(line[1]))
+ except User.DoesNotExist:
+ print line
+ continue
+
+ sum = line[5]
+ try:
+ Payment.objects.create(date=date, user=u, amount=sum, method=PaymentMethod.objects.get(name='bank collection'), original_file=filename, original_line=str(line))
+ except ValueError, e:
+ print line
+
+
+ def import_hugefile(self, filename):
+ import csv
+ from decimal import Decimal
+
+ f = open(filename, 'r')
+ r = csv.reader(f, delimiter=";")
+
+ i=0
+
+ for line in r:
+ i+=1
+ if not line[0]:
+ continue
+ pms = PaymentMethod.objects.filter(name=line[3] if not line[3] in ('sammler', 'Umlaufvermögen:2810 Bank') else 'bank collection')
+ if pms:
+ pm = pms[0]
+ else:
+ pm = PaymentMethod.objects.all()[0]
+
+ subject = line[2]
+
+ if '(' in subject and ')' in subject:
+ list_str = subject.split('(')[1].split(')')[0]
+ if list_str == '280':
+ list = [subject.split('(')[0]]
+ else:
+ list = list_str.split(',')
+ else:
+ list = [subject]
+
+ sum = line[5] if line[5] else '-'+line[4] if line[4] else '0'
+
+ sum = sum.replace(',', '.')
+
+ try:
+ sum = Decimal(sum) / len(list)
+ except Exception, e:
+ print e
+ print line
+ continue
+
+ for name in list:
+ fragments = name.split(' ')
+
+# if line[0] == '2007-10-02':
+# print name, fragments
+
+ if fragments[0] == '':
+ fragments = fragments[1:]
+
+ if len(fragments)==0:
+ print 'aaaaaaaaaahhh!!'
+ continue
+
+ if fragments[0] == 'fehlgeschlagen':
+ fragments = fragments[1:]
+
+ if 'ckzahlung' in fragments[0]:
+ fragments = fragments[1:]
+
+ if fragments[0] == 'Ewald-Oliver':
+ fragments[0] = 'Oliver'
+
+
+ if len(fragments)>1:
+ if fragments[1] in ('Sirek', 'Siereck'):
+ fragments[1] = 'Sierek'
+
+ if fragments[1] == 'Eckhardt':
+ fragments[1] = 'Eckardt'
+
+ if fragments[1] == 'Grenzfurtner':
+ fragments[1] = 'Grenzfurthner'
+
+ if fragments[1] in ('Berg',): # Berg San, Leo Findeisen
+ fragments = fragments[1:]
+
+ if fragments[1] in ('Leo',):
+ fragments = [fragments[0], fragments[2]]
+
+ if fragments[0] == 'Schreiner':
+ fragments = [fragments[1] , fragments[0]]
+
+ if fragments[1] == 'Manztos':
+ fragments[1] = 'Mantzos'
+
+ if len(fragments)>2 and fragments[2]:
+ if fragments[2] == 'Laub':
+ fragments = fragments[1:]
+
+ if(len(fragments)>1):
+ u = User.objects.filter(first_name__iexact=fragments[0], last_name__iexact=fragments[1])
+ if len(u)!=1:
+ print u, fragments
+ else:
+ Payment.objects.create(user=u[0], amount=sum, date=line[0], method=pm, original_line = str(line), original_file = filename, original_lineno = i)
+
+
+ else:
+ print 'no user found for'
+ print line
+ pass
+
+class Payment(models.Model):
+ amount = models.FloatField()
+ comment = models.CharField(max_length=200, blank=True)
+ date = models.DateField()
+ method = models.ForeignKey('PaymentMethod')
+ user = models.ForeignKey(User, null=True)
+ original_line = models.TextField(blank=True)
+ original_file = models.CharField(max_length=200, null=True)
+ original_lineno = models.IntegerField(blank=True, null=True)
+
+ objects = PaymentManager()
+
+ def __unicode__(self):
+ return u"%s, %s, %s, %s" % (self.date, self.amount, self.user.username, self.method.name)
+
+ class Meta:
+ ordering = ['date']
+
+
+class PaymentMethod(models.Model):
+ name = models.CharField(max_length=50)
+
+ def __unicode__(self):
+ return u"%s" % self.name
+
+
+class ContactInfoInline(admin.StackedInline):
+ model = ContactInfo
+ max_num = 1
+
+class PaymentInfoInline(admin.StackedInline):
+ model = PaymentInfo
+ max_num = 1
+
+class MembershipPeriodInline(admin.TabularInline):
+ model = MembershipPeriod
+
+class PaymentInline(admin.TabularInline):
+ model = Payment
+ fields=('date', 'amount', 'method')
+ ordering=('date')
+
+class MemberAdmin(admin.ModelAdmin):
+ inlines=[ContactInfoInline, PaymentInfoInline, MembershipPeriodInline, PaymentInline]
+ list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff', 'is_active')
+ list_filter = ('is_staff', 'is_superuser')
+ search_fields = ('username', 'email', 'first_name', 'last_name')
+ ordering = ('username',)
+
+
31 members/urls.py
@@ -0,0 +1,31 @@
+from django.conf.urls.defaults import *
+
+from mos.members.models import *
+
+
+info_dict = {
+ 'queryset': get_active_members(),
+ 'template_name': 'members/member_list.html',
+}
+
+urlpatterns = patterns('',
+ (r'^login/?$', 'django.contrib.auth.views.login',
+ {'template_name': 'members/member_login.html'}),
+ (r'^logout/?$', 'django.contrib.auth.views.logout',),
+ (r'^valid_user/?$', 'mos.members.views.valid_user',),
+
+ (r'^$', 'django.views.generic.list_detail.object_list', info_dict),
+ (r'^history/$', 'mos.members.views.members_history'),
+ (r'^change_password/$', 'django.contrib.auth.views.password_change',
+ {'template_name': 'members/member_update_password.html'}),
+ (r'^change_password/done/$',
+ 'django.contrib.auth.views.password_change_done',
+ {'template_name': 'members/member_update_password_done.html'}),
+ (r'^collection/$', 'mos.members.views.members_bankcollection_list'),
+ (r'^keylist/$', 'mos.members.views.members_key_list'),
+ (r'^(?P<user_username>(\w|-)+)/update/userpic/$',
+ 'mos.members.views.members_update_userpic'),
+ (r'^(?P<user_username>(\w|-)+)/update/(?P<update_type>\w+)/$',
+ 'mos.members.views.members_update'),
+ (r'^(?P<user_username>(\w|-)+)/$', 'mos.members.views.members_details'),
+)
75 members/util.py
@@ -0,0 +1,75 @@
+from mos.members.models import *
+from django.contrib.auth.models import User
+from datetime import *
+from dateutil.rrule import *
+
+
+def get_date_of_entry(user):
+ mp_list = MembershipPeriod.objects.filter(user__exact=user)\
+ .order_by('begin')[:1]
+ if len(mp_list) < 1:
+ return None
+ return mp_list[0].begin
+
+
+def get_date_of_exit(user):
+ mp_list = MembershipPeriod.objects.filter(user__exact=user)\
+ .order_by('-begin')[:1]
+ if len(mp_list) < 1:
+ return None
+ return mp_list[0].end
+
+
+class HistoryEntry:
+ month = date.today()
+ num_member = 0
+ new_member = 0
+ resigned_member = 0
+
+
+def get_list_of_history_entries():
+ he_list = {}
+
+ month_list = list(rrule(MONTHLY, dtstart=date(2006, 3, 1),
+ until=date.today()))
+ for month in month_list:
+ d = date(month.year, month.month, month.day)
+ he_list[d] = HistoryEntry()
+ he_list[d].month = d
+
+ user_list = User.objects.all()
+ for u in user_list:
+ entry = get_date_of_entry(u)
+ entry = date(entry.year, entry.month, 1)
+ num = he_list[entry].new_member
+ num += 1
+ he_list[entry].new_member = num
+
+ end = get_date_of_exit(u)
+ if end is not None:
+ end = date(end.year, end.month, 1)
+ num = he_list[end].resigned_member
+ num += 1
+ he_list[end].resigned_member = num
+
+ num = 0
+ for month in month_list:
+ he = he_list[month.date()]
+ num += he.new_member
+ num -= he.resigned_member
+ he.num_member = num
+
+ return he_list
+
+
+def generate_bank_collection_list(for_month):
+ member_list = Member.objects.all()
+ for_month = date(for_month.year, for_month.month, 1)
+ list = {}
+ for member in member_list:
+ if member.bank_collection_allowed:
+ fee = member.get_membership_fee(for_month)
+ if fee is not None and fee.amount > 0:
+ list[member] = fee.amount
+
+ return list
152 members/views.py
@@ -0,0 +1,152 @@
+__version__ = "$Revision$"
+
+from datetime import *
+
+from dateutil.rrule import *
+from django.contrib.auth.decorators import login_required
+from django.http import HttpResponse, Http404
+from django.shortcuts import render_to_response, get_object_or_404
+from django.template import RequestContext
+
+from mos.members.forms import UserEmailForm, UserNameForm, UserAdressForm,\
+ UserImageForm, UserInternListForm
+from mos.members.models import *
+from mos.members.util import *
+from django.contrib.auth import authenticate
+
+
+@login_required
+def members_history(request):
+ history_entry_list = get_list_of_history_entries()
+ history_list = []
+ months = list(rrule(MONTHLY, dtstart=date(2006, 3, 1), until=date.today()))
+ for dt in months:
+ history_list.append(history_entry_list[dt.date()])
+ history_list.reverse()
+ return render_to_response('members/members_history.html',
+ {'list': history_list},
+ context_instance=RequestContext(request))
+
+def valid_user(request):
+ #tyrsystem
+ if not request.is_secure():
+ raise Http404()
+ user_cache = authenticate(username=request.POST['user'], password=request.POST['pass'])
+ if user_cache and user_cache.is_active:
+ return HttpResponse('OK')
+ return HttpResponse('DeineMudder', status=403)
+
+
+def members_details(request, user_username, errors="", update_type=""):
+ editable = False
+ if request.user.username == user_username:
+ editable = True
+ user = get_object_or_404(User, username = user_username)
+ return render_to_response('members/members_details.html',
+ {'item': user,
+ 'ea': editable,
+ 'error_form': errors,
+ 'error_type': update_type,
+ }, context_instance=RequestContext(request))
+
+
+def members_update(request, user_username, update_type):
+ if not request.POST or not request.user.username == user_username:
+ return members_details(request, user_username,
+ "no permission to edit settings", "permission")
+ user = get_object_or_404(User, username = user_username)
+
+ error_form = False
+ error_type = False
+
+ if update_type == "email" and request.method == "POST":
+ update_form = UserEmailForm(request.POST, instance=user)
+
+ elif update_type == "name" and request.method == "POST":
+ update_form = UserNameForm(request.POST, instance=user)
+
+ elif update_type == "adress" and request.method == "POST":
+ contact_info = get_object_or_404(ContactInfo, user=user)
+ update_form = UserAdressForm(request.POST, instance=contact_info)
+
+ elif update_type == "internlist" and request.method == "POST":
+ contact_info = get_object_or_404(ContactInfo, user=user)
+ update_form = UserInternListForm(request.POST, instance=contact_info)
+
+ if update_form.is_valid():
+ update_form.save()
+ else:
+ error_form = update_form
+ error_type = update_type
+
+ return members_details(request, user_username, error_form, error_type)
+
+
+@login_required
+def members_bankcollection_list(request):
+ if request.user.is_superuser:
+ # get members that are active and have monthly collection activated
+ members_to_collect_from = get_active_members()\
+ .filter(paymentinfo__bank_collection_allowed=True)\
+ .filter(paymentinfo__bank_collection_mode__id=4)
+ # 4 = monthly
+
+
+ # build a list of collection records with name, bank data, amount
+ # and a description
+ collection_records = []
+
+ for m in members_to_collect_from:
+ debt = m.contactinfo_set.all()[0].get_debt_for_month(date.today())
+ if debt != 0:
+ pmi = m.paymentinfo_set.all()[0]
+ ci = m.contactinfo_set.all()[0]
+ collection_records.append([m.first_name, m.last_name,
+ pmi.bank_account_number,
+ pmi.bank_code,
+ pmi.bank_account_owner,
+ str(debt),
+ 'Mitgliedsbeitrag %d/%d;'
+ %(date.today().year, date.today()\
+ .month)])
+
+ #format as csv and return it
+ csv = '\r\n'.join([';'.join(x) for x in collection_records])
+
+ return HttpResponse(csv, mimetype='text/plain')
+
+
+ else:
+ return HttpResponseNotAllowed('you are not allowed to use this method')
+
+def members_key_list(request):
+ #get active members with active keys
+ members_with_keys = get_active_members().filter(contactinfo__has_active_key=True)
+ #return HttpResponse("blah", mimetype='text/plain')
+
+ #just output keys one line per key
+ text = '\r\n'.join([x.contactinfo_set.all()[0].key_id for x in members_with_keys])
+ return HttpResponse(text, mimetype='text/plain')
+
+
+def members_update_userpic(request, user_username):
+ if not request.user.username == user_username:
+ return members_details(request, user_username,
+ "no permission to edit settings", "permission")
+
+ if request.method == "POST":
+ user = get_object_or_404(User, username = user_username)
+ contact_info = get_object_or_404(ContactInfo, user=user)
+ image_form=UserImageForm(request.POST, request.FILES,
+ instance=contact_info)
+ if image_form.is_valid():
+ image_data = image_form.save()
+ image_data.save()
+ #return to userpage if upload was successfull
+ return members_details(request, user_username)
+ else:
+ image_form=UserImageForm()
+
+ return render_to_response('members/member_update_userpic.html',
+ {'form': image_form},
+ context_instance=RequestContext(request))
3 metalab/media/gallerypics/.gitignore
@@ -0,0 +1,3 @@
+# Files in this directory are not tracked.
+*
+!.gitignore
BIN metalab/media/images/atomic.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN metalab/media/images/bg.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.