Example #1
0
def ik_chsmoel(request):
    if not 'smoel' in request.FILES:
        raise ValueError, "Missing `smoel' in FILES"
    if not 'id' in request.POST:
        raise ValueError, "Missing `id' in POST"
    user = Es.by_id(request.POST['id'])
    if not request.user.may_upload_smoel_for(request.user):
        raise PermissionDenied
    original = default_storage.open(path.join(settings.SMOELEN_PHOTOS_PATH,
            str(user.name)) + ".orig", 'wb+')
    for chunk in request.FILES['smoel'].chunks():
        original.write(chunk)
    original.seek(0)
    img = Image.open(original)
    if img._getexif() is not None:
        orientation = int(img._getexif().get(274, '1')) # Orientation
        if orientation == 3:
            img = img.transpose(Image.ROTATE_180)
        elif orientation == 6:
            img = img.transpose(Image.ROTATE_270)
        elif orientation == 8:
            img = img.transpose(Image.ROTATE_90)
    width, height = resize_proportional(img.size[0], img.size[1],
                                        settings.SMOELEN_WIDTH*2,
                                        settings.SMOELEN_HEIGHT*2)
    img = img.resize((width, height), Image.ANTIALIAS)
    img.save(default_storage.open(path.join(settings.SMOELEN_PHOTOS_PATH,
            str(user.name)) + ".jpg", 'w'), "JPEG")
    Es.notify_informacie('set_smoel', request.user, entity=user)
    return redirect_to_referer(request)
Example #2
0
def ik_chsmoel(request):
    if not "smoel" in request.FILES:
        raise ValueError, _("Missende `smoel' in FILES")
    if not "id" in request.POST:
        raise ValueError, _("Missende `id' in POST")
    user = Es.by_id(request.POST["id"])
    if not user.name:
        raise ValueError, _("Entiteit heeft geen naam")
    if not request.user.may_upload_smoel_for(request.user):
        raise PermissionDenied
    original = default_storage.open(path.join(settings.SMOELEN_PHOTOS_PATH, str(user.name)) + ".orig", "wb+")
    for chunk in request.FILES["smoel"].chunks():
        original.write(chunk)
    original.seek(0)
    img = Image.open(original)
    if hasattr(img, "_getexif") and img._getexif() is not None:
        orientation = int(img._getexif().get(274, "1"))  # Orientation
        if orientation == 3:
            img = img.transpose(Image.ROTATE_180)
        elif orientation == 6:
            img = img.transpose(Image.ROTATE_270)
        elif orientation == 8:
            img = img.transpose(Image.ROTATE_90)
    width, height = resize_proportional(
        img.size[0], img.size[1], settings.SMOELEN_WIDTH * 2, settings.SMOELEN_HEIGHT * 2
    )
    img = img.resize((width, height), Image.ANTIALIAS)
    img.save(default_storage.open(path.join(settings.SMOELEN_PHOTOS_PATH, str(user.name)) + ".jpg", "w"), "JPEG")
    Es.notify_informacie("set_smoel", request.user, entity=user)
    return redirect_to_referer(request)
Example #3
0
def entity_remove_property(data, request):
    """ Removes a property of an entity (e.g. date of birth).
            Example:
            >> {action:"entity_remove_property",
                id:"4e6fcc85e60edf3dc0000270",
                key: "dateOfBirth"}
            << {ok: true}
        or: << {ok: false, error: "Permission denied"}
    """

    if 'id' not in data or not isinstance(data['id'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "id"'}
    if 'key' not in data or not isinstance(data['key'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "key"'}

    is_secretariaat = 'secretariaat' in request.user.cached_groups_names
    is_user = data['id'] == request.user.id
    if not (is_secretariaat or is_user):
        return {'ok': False, 'error': 'Permission denied'}

    e = Es.by_id(data['id'])
    if e is None:
        return {'ok': False, 'error': 'Entity not found'}

    property = data['key']
    if property == 'dateOfBirth':
        e.remove_dateOfBirth()
    elif property == 'address':
        e.remove_address()
    else:
        return {'ok': False, 'error': 'Unknown property "%s"' % property}

    return {'ok': True}
Example #4
0
def ik_chsmoel(request):
    if 'smoel' not in request.FILES:
        raise ValueError(_("Missende `smoel' in FILES"))
    if 'id' not in request.POST:
        raise ValueError(_("Missende `id' in POST"))
    user = Es.by_id(request.POST['id'])
    if not user.name:
        raise ValueError(_("Entiteit heeft geen naam"))
    if not request.user.may_upload_smoel_for(request.user):
        raise PermissionDenied
    original = default_storage.open(
        os.path.join(settings.SMOELEN_PHOTOS_PATH, str(user.name)) + ".orig",
        'wb+')
    for chunk in request.FILES['smoel'].chunks():
        original.write(chunk)
    original.seek(0)
    img = PIL.Image.open(original)
    if hasattr(img, '_getexif') and img._getexif() is not None:
        orientation = int(img._getexif().get(274, '1'))  # Orientation
        if orientation == 3:
            img = img.transpose(PIL.Image.ROTATE_180)
        elif orientation == 6:
            img = img.transpose(PIL.Image.ROTATE_270)
        elif orientation == 8:
            img = img.transpose(PIL.Image.ROTATE_90)
    width, height = resize_proportional(img.size[0], img.size[1],
                                        settings.SMOELEN_WIDTH * 2,
                                        settings.SMOELEN_HEIGHT * 2)
    img = img.resize((width, height), PIL.Image.ANTIALIAS)
    img.save(
        default_storage.open(
            os.path.join(settings.SMOELEN_PHOTOS_PATH, str(user.name)) +
            ".jpg", 'w'), "JPEG")
    Es.notify_informacie('set_smoel', request.user, entity=user)
    return redirect_to_referer(request)
Example #5
0
def entity_end_study(data, request):
    '''
    End a study at the specified date.
    '''
    if 'id' not in data or not isinstance(data['id'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "id"'}
    if 'property' not in data or not isinstance(data['property'],
                                                six.string_types):
        return {'ok': False, 'error': 'Missing argument "property"'}
    if 'value' not in data or not isinstance(data['value'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "value"'}

    if 'secretariaat' not in request.user.cached_groups_names:
        return {'ok': False, 'error': 'Permission denied'}

    e = Es.by_id(data['id'])
    if e is None:
        return {'ok': False, 'error': 'Entity not found'}
    try:
        end_date = parse_date(data['value'])
    except ValueError:
        return {'ok': False, 'error': 'Invalid date'}
    if not end_date:
        return {'ok': False, 'error': 'No valid end date given'}
    try:
        study = int(data['property'])
    except ValueError:
        return {'ok': False, 'error': 'Invalid study'}
    try:
        e.study_end(study, end_date)
    except Es.EntityException as why:
        return {'ok': False, 'error': why.message}

    return {'ok': True}
Example #6
0
def entity_end_study(data, request):
    '''
    End a study at the specified date.
    '''
    if 'id' not in data or not isinstance(data['id'], basestring):
        return {'ok': False, 'error': 'Missing argument "id"'}
    if 'study' not in data or not isinstance(data['study'], int):
        return {'ok': False, 'error': 'Missing argument "study"'}
    if 'end_date' not in data or not isinstance(data['end_date'], basestring):
        return {'ok': False, 'error': 'Missing argument "end_date"'}

    if not 'secretariaat' in request.user.cached_groups_names:
        return {'ok': False, 'error': 'Permission denied'}

    e = Es.by_id(data['id'])
    if e is None:
        return {'ok': False, 'error': 'Entity not found'}
    try:
        end_date = parse_date(data['end_date'])
    except ValueError:
        return {'ok': False, 'error': 'Invalid date'}
    if not end_date:
        return {'ok': False, 'error': 'No valid end date given'}
    try:
        e.study_end(data['study'], end_date)
    except Es.EntityException, why:
        return {'ok': False, 'error': why.message}
Example #7
0
def entity_humanName_by_id(data, request):
    """ Returns the human name of an entity by its id.  Example:

          >> {action:"entity_humanName_by_id", id="4e6fcc85e60edf3dc0000270"}
          << "Giedo Jansen (giedo) """
    e = Es.by_id(data['id'])
    return None if e is None else _humanName_of_entity(e)
Example #8
0
def entity_set_property(data, request):
    if 'id' not in data or not isinstance(data['id'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "id"'}
    if 'property' not in data or not isinstance(data['property'],
                                                six.string_types):
        return {'ok': False, 'error': 'Missing argument "property"'}
    if 'value' not in data or not isinstance(data['value'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "value"'}

    if 'secretariaat' not in request.user.cached_groups_names:
        return {'ok': False, 'error': 'Permission denied'}

    property = data['property']
    value = data['value']

    e = Es.by_id(data['id'])
    if e is None:
        return {'ok': False, 'error': 'Entity not found'}

    if property == 'description':
        e.set_description(value)
    elif property == 'humanName':
        e.set_humanName(value)
    else:
        return {'ok': False, 'error': 'Unknown property "%s"' % property}

    return {'ok': True}
Example #9
0
def entity_update_primary(data, request):
    """ Updates an entity
            >> (see below)

            << {ok: true}
          ( << {ok: false, error: "Permission denied"} ) """
    if 'id' not in data or not isinstance(data['id'], basestring):
        return {'ok': False, 'error': 'Missing argument "id"'}
    if 'type' not in data or not isinstance(data['type'], basestring):
        return {'ok': False, 'error': 'Missing argument "type"'}
    if 'new' not in data:
        return {'ok': False, 'error': 'Missing argument "new"'}
    new = data['new']
    typ = data['type']
    is_secretariaat = 'secretariaat' in request.user.cached_groups_names
    if not is_secretariaat:
        return {'ok': False, 'error': 'Permission denied'}
    if typ in ('email', 'telephone'):
        if not isinstance(new, basestring):
            return {'ok': False, 'error': '"new" should be a string'}
    elif typ == 'address':
        if not isinstance(new, dict):
            return {'ok': False, 'error': '"new" should be a dict'}
        for attr in ('street', 'number', 'zip', 'city'):
            if attr not in new or not isinstance(new[attr], basestring):
                return {
                    'ok': False,
                    'error': 'Missing argument "new.%s"' % attr
                }
    else:
        return {'ok': False, 'error': 'Unknown update type: "%s"' % typ}
    e = Es.by_id(data['id'])
    if e is None:
        return {'ok': False, 'error': 'Entity not found'}
    if (typ == 'email'):
        """ >> {action:"entity_update_primary_email",id:"4e6fcc85e60edf3dc0000270",
                    new:"*****@*****.**"} """
        if not email_re.match(new):
            return {'ok': False, 'error': 'Not valid e-mail address'}
        e.update_primary_email(new)
    elif (typ == 'telephone'):
        """ >> {action:"entity_update_primary_telephone",id:"4e6fcc85e60edf3dc0000270",
                    new:"+31611223344"} """
        if not len(new) > 9:
            return {'ok': False, 'error': 'Phone number is too short'}
        e.update_primary_telephone(new)
    elif (typ == 'address'):
        """ >> {action:"entity_update_address",id:"4e6fcc85e60edf3dc0000270",
                    street:"Street",
                    number:"23",
                    zip:"1234AA",
                    city:"Amsterdam"} """
        e.update_address(new['street'], new['number'], new['zip'], new['city'])
    else:
        return {'ok': False, 'error': 'Unknown update type: "%s"' % typ}
    giedo.sync_async(request)
    return {'ok': True}
Example #10
0
def entity_update_primary(data, request):
    """ Updates an entity
            >> (see below)

            << {ok: true}
          ( << {ok: false, error: "Permission denied"} ) """
    if 'id' not in data or not isinstance(data['id'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "id"'}
    if 'type' not in data or not isinstance(data['type'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "type"'}
    if 'new' not in data:
        return {'ok': False, 'error': 'Missing argument "new"'}
    new = data['new']
    typ = data['type']
    is_secretariaat = 'secretariaat' in request.user.cached_groups_names
    if not is_secretariaat:
        return {'ok': False, 'error': 'Permission denied'}
    if typ in ('email', 'telephone'):
        if not isinstance(new, six.string_types):
            return {'ok': False, 'error': '"new" should be a string'}
    elif typ == 'address':
        if not isinstance(new, dict):
            return {'ok': False, 'error': '"new" should be a dict'}
        for attr in ('street', 'number', 'zip', 'city'):
            if attr not in new or not isinstance(new[attr], six.string_types):
                return {
                    'ok': False,
                    'error': 'Missing argument "new.%s"' % attr}
    else:
        return {'ok': False, 'error': 'Unknown update type: "%s"' % typ}
    e = Es.by_id(data['id'])
    if e is None:
        return {'ok': False, 'error': 'Entity not found'}
    if (typ == 'email'):
        """ >> {action:"entity_update_email",id:"4e6fcc85e60edf3dc0000270",
                    new:"*****@*****.**"} """
        if not email_re.match(new):
            return {'ok': False, 'error': 'Not valid e-mail address'}
        e.update_email(new)
    elif (typ == 'telephone'):
        """ >> {action:"entity_update_telephone",id:"4e6fcc85e60edf3dc0000270",
                    new:"+31611223344"} """
        if not len(new) > 9:
            return {'ok': False, 'error': 'Phone number is too short'}
        e.update_telephone(new)
    elif (typ == 'address'):
        """ >> {action:"entity_update_address",id:"4e6fcc85e60edf3dc0000270",
                    street:"Street",
                    number:"23",
                    zip:"1234AA",
                    city:"Amsterdam"} """
        e.update_address(new['street'], new['number'], new['zip'], new['city'])
    else:
        return {'ok': False, 'error': 'Unknown update type: "%s"' % typ}
    giedo.sync_async(request)
    return {'ok': True}
Example #11
0
def event_detail(request, name):
    # First, find the event.
    event = subscr_Es.event_by_name(name)
    if event is None:
        raise Http404
    # Has the user already subscribed?
    subscription = event.get_subscription(request.user)
    # What are our permissions?
    has_read_access = event.has_read_access(request.user)
    has_write_access = event.has_write_access(request.user)
    if request.method == 'POST' and 'subscribe' in request.POST:
        if not event.is_open:
            raise PermissionDenied
        if subscription is not None and subscription.subscribed:
            messages.error(request, "Je bent al aangemeld")
        else:
            notes = request.POST['notes']
            subscription = event.subscribe(request.user, notes)
        return HttpResponseRedirect(reverse('event-detail',
                                            args=(event.name,)))
    elif request.method == 'POST' and 'invite' in request.POST:
        if not event.is_open:
            raise PermissionDenied
        # Find the other user
        user = Es.by_id(request.POST['who'])
        if not user or not user.is_user:
            raise Http404
        # Is the other already subscribed?
        if event.get_subscription(user) is not None:
            messages.error(request, "%s is al aangemeld" % user.full_name)
        else:
            notes = request.POST['notes']
            other_subscription = event.invite(user, notes, request.user)
        return HttpResponseRedirect(reverse('event-detail',
                                            args=(event.name,)))

    users = filter(lambda u: event.get_subscription(u) is None and \
                             u != request.user,
                   Es.by_name('leden').get_members())
    users.sort(key=lambda u: unicode(u.humanName))
    subscriptions = event.subscriptions
    subscriptions.sort(key=lambda s: s.date)
    invitations = event.invitations
    invitations.sort(key=lambda i: i.date)

    ctx = {'object': event,
           'user': request.user,
           'users': users,
           'subscription': subscription,
           'subscriptions': subscriptions,
           'invitations': invitations,
           'has_read_access': has_read_access,
           'has_write_access': has_write_access}
    return render_to_response('subscriptions/event_detail.html', ctx,
            context_instance=RequestContext(request))
Example #12
0
def user_reset_password(request, _id):
    if not "secretariaat" in request.user.cached_groups_names:
        raise PermissionDenied
    u = Es.by_id(_id).as_user()
    if not u.is_active:
        raise ValueError, _("Gebruiker is niet geactiveerd")
    pwd = pseudo_randstr()
    u.set_password(pwd)
    giedo.change_password(str(u.name), pwd, pwd)
    render_then_email("leden/reset-password.mail.txt", u, {"user": u, "password": pwd})
    messages.info(request, _("Wachtwoord gereset!"))
    return redirect_to_referer(request)
Example #13
0
def relation_begin(request):
    # TODO We should use Django forms, or better: use sweet Ajax
    d = {}
    for t in ('who', 'with', 'how'):
        if t not in request.POST:
            raise ValueError, "Missing attr %s" % t
        if t == 'how' and (not request.POST[t] or
                               request.POST[t] == 'null'):
            d[t] = None
        else:
            d[t] = _id(request.POST[t])
    if not Es.user_may_begin_relation(request.user, d['who'], d['with'],
                                                                d['how']):
        raise PermissionDenied
    # Check whether such a relation already exists
    dt = now()
    ok = False
    try:
        next(Es.query_relations(who=d['who'], _with=d['with'],
            how=d['how'], _from=dt, until=DT_MAX))
    except StopIteration:
        ok = True
    if not ok:
        raise ValueError, "This relation already exists"
    # Add the relation!
    Es.add_relation(d['who'], d['with'], d['how'], dt, DT_MAX)
    # Notify informacie
    if request.user._id == d['who']:
        Es.notify_informacie("%s heeft zich ingeschreven als %s %s" % (
                            request.user.full_name,
                            Es.by_id(d['how']).humanName if d['how'] else 'lid',
                            Es.by_id(d['with']).humanName.genitive))
    else:
        # TODO (rik) leave out 'als lid'
        Es.notify_informacie("%s is nu %s %s" % (
                            Es.by_id(d['who']).humanName,
                            Es.by_id(d['how']).humanName if d['how'] else 'lid',
                            Es.by_id(d['with']).humanName.genitive))
    giedo.sync_async(request)
    return redirect_to_referer(request)
Example #14
0
def user_reset_password(request, _id):
    if not 'secretariaat' in request.user.cached_groups_names:
        raise PermissionDenied
    u = Es.by_id(_id).as_user()
    pwd = pseudo_randstr()
    u.set_password(pwd)
    giedo.change_password(str(u.name), pwd, pwd)
    render_then_email("leden/reset-password.mail.txt",
                        u.canonical_full_email, {
                            'user': u,
                            'password': pwd})
    request.user.push_message("Wachtwoord gereset!")
    return redirect_to_referer(request)
Example #15
0
def _get_group_and_tag(request):
    """
    Helper for tag and untag to quickly get the group and tag from a request or
    raise an error on invalid input.
    """
    if "group" not in request.POST:
        raise ValueError("Missing group")
    group = Es.by_id(request.POST["group"])
    if not group:
        raise Http404("group does not exist")
    if not group.is_group:
        raise ValueError(_("'group' is niet een groep"))

    if "tag" not in request.POST:
        raise ValueError("Missing tag")
    tag = Es.by_id(request.POST["tag"])
    if not tag:
        raise Http404("tag does not exist")
    if not tag.is_tag:
        raise ValueError(_("'tag' is niet een stempel"))

    return group, tag
def main():
    count = 0
    for vacancy in Vacancy.all():
        if vacancy.assignee_id is None:
            continue
        worker_data = wcol.find_one({'_id': vacancy.assignee_id})
        if worker_data is None:
            continue
        user = Es.by_id(worker_data['user'])
        vacancy._data['assignee'] = _id(user)
        vacancy.save()
        count += 1
    print 'Converted %d vacancies.' % count
def main():
    count = 0
    for vacancy in Vacancy.all():
        if vacancy.assignee_id is None:
            continue
        worker_data = wcol.find_one({'_id': vacancy.assignee_id})
        if worker_data is None:
            continue
        user = Es.by_id(worker_data['user'])
        vacancy._data['assignee'] = _id(user)
        vacancy.save()
        count += 1
    print('Converted %d vacancies.' % count)
Example #18
0
def _get_group_and_tag(request):
    '''
    Helper for tag and untag to quickly get the group and tag from a request or
    raise an error on invalid input.
    '''
    if 'group' not in request.POST:
        raise ValueError('Missing group')
    group = Es.by_id(request.POST['group'])
    if not group:
        raise Http404('group does not exist')
    if not group.is_group:
        raise ValueError(_("'group' is niet een groep"))

    if 'tag' not in request.POST:
        raise ValueError('Missing tag')
    tag = Es.by_id(request.POST['tag'])
    if not tag:
        raise Http404('tag does not exist')
    if not tag.is_tag:
        raise ValueError(_("'tag' is niet een stempel"))

    return group, tag
Example #19
0
def _get_group_and_tag(request):
    '''
    Helper for tag and untag to quickly get the group and tag from a request or
    raise an error on invalid input.
    '''
    if 'group' not in request.POST:
        raise ValueError('Missing group')
    group = Es.by_id(request.POST['group'])
    if not group:
        raise Http404('group does not exist')
    if not group.is_group:
        raise ValueError(_("'group' is niet een groep"))

    if 'tag' not in request.POST:
        raise ValueError('Missing tag')
    tag = Es.by_id(request.POST['tag'])
    if not tag:
        raise Http404('tag does not exist')
    if not tag.is_tag:
        raise ValueError(_("'tag' is niet een stempel"))

    return group, tag
Example #20
0
def user_reset_password(request, _id):
    if not 'secretariaat' in request.user.cached_groups_names:
        raise PermissionDenied
    u = Es.by_id(_id).as_user()
    pwd = pseudo_randstr()
    u.set_password(pwd)
    giedo.change_password(str(u.name), pwd, pwd)
    render_then_email("leden/reset-password.mail.txt",
                        u.canonical_full_email, {
                            'user': u,
                            'password': pwd})
    request.user.push_message("Wachtwoord gereset!")
    return redirect_to_referer(request)
Example #21
0
def entity_detail(request, name=None, _id=None, type=None):
    if name is not None:
        e = Es.by_name(name)
    else:
        e = Es.by_id(_id)
    if e is None:
        raise Http404
    if type and not type in e.types:
        raise ValueError, "Entity is not a %s" % type
    if not type:
        type = e.type
    if not type in Es.TYPE_MAP:
        raise ValueError, "Unknown entity type"
    return globals()['_'+type+'_detail'](request, getattr(e, 'as_'+type)())
Example #22
0
def entity_detail(request, name=None, _id=None, type=None):
    if name is not None:
        e = Es.by_name(name)
    else:
        e = Es.by_id(_id)
    if e is None:
        raise Http404
    if type and not type in e.types:
        raise ValueError, "Entity is not a %s" % type
    if not type:
        type = e.type
    if not type in Es.TYPE_MAP:
        raise ValueError, "Unknown entity type"
    return globals()['_'+type+'_detail'](request, getattr(e, 'as_'+type)())
Example #23
0
def entity_detail(request, name=None, _id=None, type=None):
    if name is not None:
        e = Es.by_name(name)
    else:
        e = Es.by_id(_id)
    if e is None:
        raise Http404
    if type and not type in e.types:
        raise ValueError, _("Entiteit is niet een %s") % type
    if not type:
        type = e.type
    if not type in Es.TYPE_MAP:
        raise ValueError, _("Onbekende entiteit type")
    return globals()["_" + type + "_detail"](request, getattr(e, "as_" + type)())
Example #24
0
def main():
    leden = frozenset(Es.by_name('leden').get_members())
    han = Es.by_id('4e6fcc85e60edf3dc0000015')
    for m in sorted(Es.by_institute(han), key=lambda x: str(x.name)):
        if not m in leden:
            continue
        ok = False
        for study in m.studies:
            if study['institute'] != han:
                continue
            if study['until'] is None or study['until'] >= Es.now():
                ok = True
        print "%-30s %-10s %s" % (m.full_name, study['number'],
                        unicode(study['study'].humanName))
Example #25
0
def main():
    leden = frozenset(Es.by_name('leden').get_members())
    han = Es.by_id('4e6fcc85e60edf3dc0000015')
    for m in sorted(Es.by_institute(han), key=lambda x: str(x.name)):
        if not m in leden:
            continue
        ok = False
        for study in m.studies:
            if study['institute'] != han:
                continue
            if study['until'] is None or study['until'] >= Es.now():
                ok = True
        print "%-30s %-10s %s" % (m.full_name, study['number'],
                                  unicode(study['study'].humanName))
Example #26
0
def note_add(request):
    if not 'secretariaat' in request.user.cached_groups_names:
        raise PermissionDenied
    if 'on' not in request.POST or 'note' not in request.POST:
        raise ValueError, "missing `on' or `note'"
    on = Es.by_id(_id(request.POST['on']))
    if on is None:
        raise Http404
    on.add_note(request.POST['note'], request.user)
    render_then_email("leden/new-note.mail.txt",
                        Es.by_name('secretariaat').canonical_full_email, {
                            'user': request.user,
                            'note': request.POST['note'],
                            'on': on})
    return redirect_to_referer(request)
Example #27
0
def user_reset_password(request, _id):
    if 'secretariaat' not in request.user.cached_groups_names:
        raise PermissionDenied
    u = Es.by_id(_id).as_user()
    if not u.is_active:
        raise ValueError(_("Gebruiker is niet geactiveerd"))
    pwd = pseudo_randstr()
    u.set_password(pwd)
    giedo.change_password(str(u.name), pwd, pwd)
    render_then_email("leden/reset-password.mail.html", u, {
        'user': u,
        'password': pwd
    })
    messages.info(request, _("Wachtwoord gereset!"))
    return redirect_to_referer(request)
Example #28
0
def ik_chsmoel(request):
    if not 'secretariaat' in request.user.cached_groups_names:
        raise PermissionDenied
    if not 'id' in request.POST:
        raise ValueError, "Missing `id' in POST"
    if not 'smoel' in request.FILES:
        raise ValueError, "Missing `smoel' in FILES"
    user = Es.by_id(request.POST['id'])
    img = Image.open(request.FILES['smoel'])
    img = img.resize((settings.SMOELEN_WIDTH,
        int(float(settings.SMOELEN_WIDTH) / img.size[0] * img.size[1])),
            Image.ANTIALIAS)
    img.save(default_storage.open(path.join(settings.SMOELEN_PHOTOS_PATH,
            str(user.name)) + ".jpg", 'w'), "JPEG")
    return redirect_to_referer(request)
def main():
    # Fetch year overrides
    print 'loading year overrides ...'
    year_overrides = {}
    for t in Es.bearers_by_tag_id(Es.id_by_name('!year-overrides'), _as=Es.Tag):
        year_overrides[(t._data['year-override']['type'],
                        t._data['year-override']['year'])] = t._id
    assert year_overrides
    years = [t[1] for t in year_overrides.keys()]
    min_year, max_year = min(years), max(years)
    leden_id = Es.id_by_name('leden')
    print 'checking ...'
    for year in xrange(min_year+1, max_year+1):
        start_of_year = Es.year_to_range(year)[0]
        informal_start = start_of_year - datetime.timedelta(3*365/12)
        for rel in Es.rcol.find({'with': leden_id,
                                 'from': {'$gt': informal_start,
                                          '$lt': start_of_year}}):
            if year_overrides[(False, year-1)] in rel.get('tags', ()):
                continue
            if 'tags' not in rel:
                rel['tags'] = []
            rel['tags'].append(year_overrides[(False, year-1)])
            print Es.by_id(rel['who']).name, year
Example #30
0
def note_add(request):
    if not "secretariaat" in request.user.cached_groups_names:
        raise PermissionDenied
    if "on" not in request.POST or "note" not in request.POST:
        raise ValueError, _("missende `on' of `note'")
    on = Es.by_id(_id(request.POST["on"]))
    if on is None:
        raise Http404
    on.add_note(request.POST["note"], request.user)
    render_then_email(
        "leden/new-note.mail.txt",
        Es.by_name("secretariaat").canonical_full_email,
        {"user": request.user, "note": request.POST["note"], "on": on},
    )
    return redirect_to_referer(request)
Example #31
0
def note_add(request):
    if not 'secretariaat' in request.user.cached_groups_names:
        raise PermissionDenied
    if 'on' not in request.POST or 'note' not in request.POST:
        raise ValueError, "missing `on' or `note'"
    on = Es.by_id(_id(request.POST['on']))
    if on is None:
        raise Http404
    on.add_note(request.POST['note'], request.user)
    render_then_email("leden/new-note.mail.txt",
                        Es.by_name('secretariaat').canonical_full_email, {
                            'user': request.user,
                            'note': request.POST['note'],
                            'on': on})
    return redirect_to_referer(request)
Example #32
0
def ik_chsmoel(request):
    if not 'secretariaat' in request.user.cached_groups_names:
        raise PermissionDenied
    if not 'id' in request.POST:
        raise ValueError, "Missing `id' in POST"
    if not 'smoel' in request.FILES:
        raise ValueError, "Missing `smoel' in FILES"
    user = Es.by_id(request.POST['id'])
    img = Image.open(request.FILES['smoel'])
    smoelen_width = settings.SMOELEN_WIDTH * 2
    img = img.resize((smoelen_width,
        int(float(smoelen_width) / img.size[0] * img.size[1])),
            Image.ANTIALIAS)
    img.save(default_storage.open(path.join(settings.SMOELEN_PHOTOS_PATH,
            str(user.name)) + ".jpg", 'w'), "JPEG")
    return redirect_to_referer(request)
Example #33
0
def entity_detail(request, name=None, _id=None, type=None):
    if name is not None:
        e = Es.by_name(name)
    else:
        e = Es.by_id(_id)
    if e is None:
        raise Http404
    if type and type not in e.types:
        # Different type than expected based on the URL.
        return redirect(e)
    if not type:
        type = e.type
    if type not in Es.TYPE_MAP:
        raise ValueError(_("Onbekende entiteit type"))
    return globals()['_' + type + '_detail'](request, getattr(e,
                                                              'as_' + type)())
Example #34
0
def note_add(request):
    if not 'secretariaat' in request.user.cached_groups_names:
        raise PermissionDenied
    if 'on' not in request.POST or 'note' not in request.POST:
        raise ValueError, "missing `on' or `note'"
    on = Es.by_id(_id(request.POST['on']))
    if on is None:
        raise Http404
    on.add_note(request.POST['note'], request.user)
    email = EmailMessage(
        "Nieuwe notitie",
        "Door %s is de volgende notitie geplaatst op %s:\r\n\r\n%s" % (
            request.user.full_name, unicode(on.humanName),
            request.POST['note']),
        'Karpe Noktem\'s ledenadministratie <*****@*****.**>',
        [Es.by_name('secretariaat').canonical_email]).send()
    return redirect_to_referer(request)
Example #35
0
def note_add(request):
    if 'secretariaat' not in request.user.cached_groups_names:
        raise PermissionDenied
    if 'on' not in request.POST or 'note' not in request.POST:
        raise ValueError(_("missende `on' of `note'"))
    on = Es.by_id(_id(request.POST['on']))
    if on is None:
        raise Http404
    note = on.add_note(request.POST['note'], request.user)
    render_then_email("leden/new-note.mail.html",
                      Es.by_name('secretariaat').canonical_full_email, {
                          'user': request.user,
                          'note': request.POST['note'],
                          'on': on},
                      headers={
                          'In-Reply-To': note.messageId,
                          'References': note.messageId})
    return redirect_to_referer(request)
Example #36
0
def entity_detail(request, name=None, _id=None, type=None):
    if name is not None:
        e = Es.by_name(name)
    else:
        e = Es.by_id(_id)
    if e is None:
        raise Http404
    if type and type not in e.types:
        # Different type than expected based on the URL.
        return redirect(e)
    if not type:
        type = e.type
    if type not in Es.TYPE_MAP:
        raise ValueError(_("Onbekende entiteit type"))
    return globals()['_' + type + '_detail'](
        request,
        getattr(e, 'as_' + type)()
    )
Example #37
0
def note_add(request):
    if 'secretariaat' not in request.user.cached_groups_names:
        raise PermissionDenied
    if 'on' not in request.POST or 'note' not in request.POST:
        raise ValueError(_("missende `on' of `note'"))
    on = Es.by_id(_id(request.POST['on']))
    if on is None:
        raise Http404
    note = on.add_note(request.POST['note'], request.user)
    render_then_email("leden/new-note.mail.html",
                      Es.by_name('secretariaat').canonical_full_email, {
                          'user': request.user,
                          'note': request.POST['note'],
                          'on': on
                      },
                      headers={
                          'In-Reply-To': note.messageId,
                          'References': note.messageId
                      })
    return redirect_to_referer(request)
Example #38
0
def user_reset_password(request, _id):
    if not 'secretariaat' in request.user.cached_groups_names:
        raise PermissionDenied
    u = Es.by_id(_id).as_user()
    pwd = pseudo_randstr()
    u.set_password(pwd)
    giedo.change_password(str(u.name), pwd, pwd)
    email = EmailMessage(
        "[KN] Nieuw wachtwoord",
        ("Beste %s,\n\n"+
         "Jouw wachtwoord is gereset.  Je kunt inloggen met:\n"+
         "  gebruikersnaam     %s\n"+
         "  wachtwoord         %s\n\n"+
         "Met een vriendelijke groet,\n\n"+
         "  Het Karpe Noktem Smoelenboek") % (
              u.first_name, str(u.name), pwd),
        'Karpe Noktem\'s ledenadministratie <*****@*****.**>',
        [u.canonical_email])
    email.send()
    request.user.push_message("Wachtwoord gereset!")
    return redirect_to_referer(request)
Example #39
0
def entity_update_visibility(data, request):
    """ Updates the visibility of a part of an entity (e.g. email,
        phone number...)
            Example:
            >> {action:"entity_update_visibility",
                id:"4e6fcc85e60edf3dc0000270",
                property: "telephone",
                visible: False}
            << {ok: true}
        or: << {ok: false, error: "Permission denied"}
    """

    if 'id' not in data or not isinstance(data['id'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "id"'}
    if 'property' not in data or not isinstance(data['property'],
                                                six.string_types):
        return {'ok': False, 'error': 'Missing argument "property"'}
    if 'visible' not in data or not isinstance(data['visible'], bool):
        return {'ok': False, 'error': 'Missing argument "visible"'}

    is_secretariaat = 'secretariaat' in request.user.cached_groups_names
    is_user = data['id'] == request.user.id
    if not (is_secretariaat or is_user):
        return {'ok': False, 'error': 'Permission denied'}

    property = data['property']
    visible = data['visible']

    if property not in ['telephone']:
        return {'ok': False, 'error': 'Unknown property "%s"' % property}

    e = Es.by_id(data['id'])
    if e is None:
        return {'ok': False, 'error': 'Entity not found'}

    e.update_visibility_preference(property, visible)

    giedo.sync_async(request)
    return {'ok': True}
Example #40
0
def entity_update_visibility(data, request):
    """ Updates the visibility of a part of an entity (e.g. email,
        phone number...)
            Example:
            >> {action:"entity_update_visibility",
                id:"4e6fcc85e60edf3dc0000270",
                property: "telephone",
                value: False}
            << {ok: true}
        or: << {ok: false, error: "Permission denied"}
    """

    if 'id' not in data or not isinstance(data['id'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "id"'}
    if 'key' not in data or not isinstance(data['key'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "key"'}
    if 'value' not in data or not isinstance(data['value'], bool):
        return {'ok': False, 'error': 'Missing argument "value"'}

    is_secretariaat = 'secretariaat' in request.user.cached_groups_names
    is_user = data['id'] == request.user.id
    if not (is_secretariaat or is_user):
        return {'ok': False, 'error': 'Permission denied'}

    property = data['key']
    value = data['value']

    if property not in ['telephone', 'dateOfBirth']:
        return {'ok': False, 'error': 'Unknown property "%s"' % property}

    e = Es.by_id(data['id'])
    if e is None:
        return {'ok': False, 'error': 'Entity not found'}

    e.update_visibility_preference(property, value)

    return {'ok': True}
Example #41
0
def entity_set_property(data, request):
    if 'id' not in data or not isinstance(data['id'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "id"'}
    if 'key' not in data or not isinstance(data['key'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "key"'}
    if 'value' not in data or not isinstance(data['value'], six.string_types):
        return {'ok': False, 'error': 'Missing argument "value"'}

    if 'secretariaat' not in request.user.cached_groups_names:
        return {'ok': False, 'error': 'Permission denied'}

    property = data['key']
    value = data['value']

    e = Es.by_id(data['id'])
    if e is None:
        return {'ok': False, 'error': 'Entity not found'}

    if property == 'description':
        e.set_description(value)
    elif property == 'humanName':
        e.set_humanName(value)
    elif property == 'dateOfBirth':
        try:
            dateOfBirth = parse_date(value)
            if dateOfBirth is None:
                raise ValueError('cannot parse date')
            dateOfBirth = datetime.datetime.combine(
                dateOfBirth, datetime.datetime.min.time())
        except ValueError as e:
            return {'ok': False, 'error': str(e)}
        e.set_dateOfBirth(dateOfBirth)
    else:
        return {'ok': False, 'error': 'Unknown property "%s"' % property}

    return {'ok': True}
Example #42
0
def event_new_or_edit(request, edit=None):
    superuser = subscr_Es.is_superuser(request.user)
    if edit is not None:
        e = subscr_Es.event_by_name(edit)
        if e is None:
            raise Http404
        if not superuser and not request.user.is_related_with(e.owner) and \
                not _id(e.owner) == request.user._id:
            raise PermissionDenied
    AddEventForm = get_add_event_form(request.user, superuser, bool(edit))
    if request.method == 'POST':
        form = AddEventForm(request.POST)
        if form.is_valid():
            fd = form.cleaned_data
            # The superuser may do everything, and when you yourself are the
            # owner that's always okay too.
            if not superuser and fd['owner'] != request.user.id:
                # Check some more constraints.
                owner = Es.by_id(fd['owner'])
                if not request.user.is_related_with(owner):
                    raise PermissionDenied(
                        _('Gebruiker niet verwant met eigenaar'))
                if not subscr_Es.may_set_owner(request.user, owner):
                    raise PermissionDenied(_('Mag deze eigenaar niet kiezen'))
            d = {
                'date':
                date_to_dt(fd['date']),
                'owner':
                _id(fd['owner']),
                'description':
                fd['description'],
                'description_html':
                kn.utils.markdown.parser.convert(fd['description']),
                'has_public_subscriptions':
                fd['has_public_subscriptions'],
                'may_unsubscribe':
                fd['may_unsubscribe'],
                'humanName':
                fd['humanName'],
                'createdBy':
                request.user._id,
                'cost':
                str(fd['cost']),
                'max_subscriptions':
                fd['max_subscriptions'],
                'is_official':
                superuser
            }
            if edit is None:
                name = fd['name']
                # If not secretariaat, then prefix name with the username
                if fd['owner'] == request.user.id:
                    prefix = str(request.user.name) + '-'
                else:
                    prefix = str(Es.by_id(fd['owner']).name) + '-'
                if (not superuser and not name.startswith(prefix)
                        and (edit is None or e.name != name)):
                    name = prefix + name
                d['name'] = name
                d['is_open'] = True  # default for new events
                e = subscr_Es.Event(d)
            else:
                e.update(d, request.user, save=False)
            e.save()
            render_then_email(
                'subscriptions/' + ('event-edited' if edit else 'new-event') +
                '.mail.html',
                Es.by_name('secretariaat').canonical_full_email,
                {
                    'event': e,
                    'user': request.user
                },
                headers={
                    'In-Reply-To': e.messageId,
                    'References': e.messageId,
                },
            )
            return HttpResponseRedirect(
                reverse('event-detail', args=(e.name, )))
    elif edit is None:
        form = AddEventForm()
    else:
        d = e._data
        form = AddEventForm(d)
    ctx = {'form': form, 'edit': edit}
    return render(request, 'subscriptions/event_new_or_edit.html', ctx)
Example #43
0
 def get_user(self):
     return Es.by_id(self.user_id)
Example #44
0
 def get_by(self):
     return Es.by_id(self._data['by'])
Example #45
0
def event_new_or_edit(request, edit=None):
    superuser = subscr_Es.is_superuser(request.user)
    if edit is not None:
        e = subscr_Es.event_by_name(edit)
        if e is None:
            raise Http404
        if not superuser and not request.user.is_related_with(e.owner) and \
                not _id(e.owner) == request.user._id:
            raise PermissionDenied
    AddEventForm = get_add_event_form(request.user, superuser, bool(edit))
    if request.method == 'POST':
        form = AddEventForm(request.POST)
        if form.is_valid():
            fd = form.cleaned_data
            # The superuser may do everything, and when you yourself are the
            # owner that's always okay too.
            if not superuser and fd['owner'] != request.user.id:
                # Check some more constraints.
                owner = Es.by_id(fd['owner'])
                if not request.user.is_related_with(owner):
                    raise PermissionDenied(
                        _('Gebruiker niet verwant met eigenaar')
                    )
                if not subscr_Es.may_set_owner(request.user, owner):
                    raise PermissionDenied(_('Mag deze eigenaar niet kiezen'))
            d = {
                'date': date_to_dt(fd['date']),
                'owner': _id(fd['owner']),
                'description': fd['description'],
                'description_html': kn.utils.markdown.parser.convert(
                    fd['description']),
                'has_public_subscriptions': fd['has_public_subscriptions'],
                'may_unsubscribe': fd['may_unsubscribe'],
                'humanName': fd['humanName'],
                'createdBy': request.user._id,
                'cost': str(fd['cost']),
                'max_subscriptions': fd['max_subscriptions'],
                'is_official': superuser}
            if edit is None:
                name = fd['name']
                # If not secretariaat, then prefix name with the username
                if fd['owner'] == request.user.id:
                    prefix = str(request.user.name) + '-'
                else:
                    prefix = str(Es.by_id(fd['owner']).name) + '-'
                if (not superuser and not name.startswith(prefix) and (
                        edit is None or e.name != name)):
                    name = prefix + name
                d['name'] = name
                d['is_open'] = True  # default for new events
                e = subscr_Es.Event(d)
            else:
                e.update(d, request.user, save=False)
            e.save()
            render_then_email(
                'subscriptions/' +
                ('event-edited' if edit else 'new-event') + '.mail.html',
                Es.by_name('secretariaat').canonical_full_email, {
                    'event': e,
                    'user': request.user
                },
                headers={
                    'In-Reply-To': e.messageId,
                    'References': e.messageId,
                },
            )
            return HttpResponseRedirect(
                reverse('event-detail', args=(e.name,)))
    elif edit is None:
        form = AddEventForm()
    else:
        d = e._data
        form = AddEventForm(d)
    ctx = {'form': form,
           'edit': edit}
    return render(request, 'subscriptions/event_new_or_edit.html', ctx)
Example #46
0
 def inviter(self):
     return Es.by_id(self._data.get('inviter'))
Example #47
0
 def createdBy(self):
     return Es.by_id(self._data['createdBy'])
Example #48
0
 def owner(self):
     return Es.by_id(self._data['owner'])
Example #49
0
 def subscribedBy(self):
     if not 'subscribedBy' in self._data:
         return None
     return Es.by_id(self._data['subscribedBy'])
Example #50
0
 def user(self):
     return Es.by_id(self._data['user'])
Example #51
0
 def subscriber(self):
     subscriber = self.lastMutation.get('subscriber')
     if subscriber is not None:
         return Es.by_id(subscriber)
     return self.user
Example #52
0
 def get_assignee(self):
     aid = self.assignee_id
     if aid is None:
         return None
     return Es.by_id(self.assignee_id)
Example #53
0
def event_detail(request, name):
    # First, find the event.
    event = subscr_Es.event_by_name(name)
    if event is None:
        raise Http404
    # Has the user already subscribed?
    subscription = event.get_subscription(request.user)
    # What are our permissions?
    has_read_access = event.has_read_access(request.user)
    has_write_access = event.has_write_access(request.user)
    if request.method == 'POST' and 'subscribe' in request.POST:
        if not event.can_subscribe:
            raise PermissionDenied
        if subscription is not None and subscription.subscribed:
            messages.error(request, _("Je bent al aangemeld"))
        else:
            notes = request.POST['notes']
            subscription = event.subscribe(request.user, notes)
        return HttpResponseRedirect(
            reverse('event-detail', args=(event.name, )))
    elif request.method == 'POST' and 'unsubscribe' in request.POST:
        if not event.can_unsubscribe:
            raise PermissionDenied
        if not subscription.subscribed:
            messages.error(request, _("Je bent al afgemeld"))
        else:
            notes = request.POST['notes']
            subscription = event.unsubscribe(request.user, notes)
        return HttpResponseRedirect(
            reverse('event-detail', args=(event.name, )))
    elif request.method == 'POST' and 'invite' in request.POST:
        if not event.is_open:
            raise PermissionDenied
        # Find the other user
        user = Es.by_id(request.POST['who'])
        if not user or not user.is_user:
            raise Http404
        # Is the other already subscribed?
        if event.get_subscription(user) is not None:
            messages.error(request, _("%s is al aangemeld") % user.full_name)
        else:
            notes = request.POST['notes']
            event.invite(user, notes, request.user)
        return HttpResponseRedirect(
            reverse('event-detail', args=(event.name, )))

    users = list(
        filter(
            lambda u: event.get_subscription(u) is None and u != request.user,
            Es.by_name('leden').get_members()))
    users.sort(key=lambda u: six.text_type(u.humanName))
    listSubscribed = sorted(event.listSubscribed, key=lambda s: s.date)
    listUnsubscribed = sorted(event.listUnsubscribed, key=lambda s: s.date)
    listInvited = sorted(event.listInvited, key=lambda s: s.date)

    ctx = {
        'object': event,
        'user': request.user,
        'users': users,
        'subscription': subscription,
        'listSubscribed': listSubscribed,
        'listUnsubscribed': listUnsubscribed,
        'listInvited': listInvited,
        'has_read_access': has_read_access,
        'has_write_access': has_write_access
    }
    return render(request, 'subscriptions/event_detail.html', ctx)
Example #54
0
def event_detail(request, name):
    # First, find the event.
    event = subscr_Es.event_by_name(name)
    if event is None:
        raise Http404
    # Has the user already subscribed?
    subscription = event.get_subscription(request.user)
    # What are our permissions?
    has_read_access = event.has_read_access(request.user)
    has_write_access = event.has_write_access(request.user)
    if request.method == 'POST' and 'subscribe' in request.POST:
        if not event.can_subscribe:
            raise PermissionDenied
        if subscription is not None and subscription.subscribed:
            messages.error(request, _("Je bent al aangemeld"))
        else:
            notes = request.POST['notes']
            subscription = event.subscribe(request.user, notes)
        return HttpResponseRedirect(reverse('event-detail',
                                            args=(event.name,)))
    elif request.method == 'POST' and 'unsubscribe' in request.POST:
        if not event.can_unsubscribe:
            raise PermissionDenied
        if not subscription.subscribed:
            messages.error(request, _("Je bent al afgemeld"))
        else:
            notes = request.POST['notes']
            subscription = event.unsubscribe(request.user, notes)
        return HttpResponseRedirect(reverse('event-detail',
                                            args=(event.name,)))
    elif request.method == 'POST' and 'invite' in request.POST:
        if not event.is_open:
            raise PermissionDenied
        # Find the other user
        user = Es.by_id(request.POST['who'])
        if not user or not user.is_user:
            raise Http404
        # Is the other already subscribed?
        if event.get_subscription(user) is not None:
            messages.error(request, _("%s is al aangemeld") % user.full_name)
        else:
            notes = request.POST['notes']
            event.invite(user, notes, request.user)
        return HttpResponseRedirect(reverse('event-detail',
                                            args=(event.name,)))

    users = list(filter(
        lambda u: event.get_subscription(u) is None and u != request.user,
        Es.by_name('leden').get_members()
    ))
    users.sort(key=lambda u: six.text_type(u.humanName))
    listSubscribed = sorted(event.listSubscribed, key=lambda s: s.date)
    listUnsubscribed = sorted(event.listUnsubscribed, key=lambda s: s.date)
    listInvited = sorted(event.listInvited, key=lambda s: s.date)

    ctx = {'object': event,
           'user': request.user,
           'users': users,
           'subscription': subscription,
           'listSubscribed': listSubscribed,
           'listUnsubscribed': listUnsubscribed,
           'listInvited': listInvited,
           'has_read_access': has_read_access,
           'has_write_access': has_write_access}
    return render(request, 'subscriptions/event_detail.html', ctx)
def main():
    parser = argparse.ArgumentParser(description="Prepare for the next year")
    parser.add_argument('--apply',
                        action='store_true',
                        help=('Apply the changes.  By default the changes '
                              'are only displayed'))
    args = parser.parse_args()

    # Fetch year overrides
    while True:
        years, year_overrides, min_year, max_year = load_year_overrides()
        assert len(years) == max_year - min_year + 1  # year-override missing?
        current_year = Es.date_to_year(datetime.datetime.now())
        if current_year == max_year:
            print(' adding year-overrides for year', current_year + 1)
            if args.apply:
                create_year_overrides_for(current_year + 1)
            continue
        break

    today = datetime.datetime.today()
    max_year = today.year - Es.DT_MIN.year + 1

    # Fetch ids of all current members
    leden_id = Es.id_by_name('leden')

    print('If you became a member after june,'
          ' you should be in the next year ...')
    for year in range(min_year + 1, max_year):
        start_of_year = Es.year_to_range(year)[0]
        informal_start = start_of_year - datetime.timedelta(3 * 365 / 12)
        for rel in Es.rcol.find({
                'with': leden_id,
                'from': {
                    '$gt': informal_start,
                    '$lt': start_of_year
                }
        }):
            if year_overrides[(False, year - 1)] in rel.get('tags', ()):
                continue
            if 'tags' not in rel:
                rel['tags'] = []
            rel['tags'].append(year_overrides[(False, year - 1)])
            print(' ', Es.by_id(rel['who']).name, '-' + str(year - 1))
            if args.apply:
                Es.rcol.save(rel)

    print('Any relation that starts near the change of year, should start')
    print('exactly on the change of year ...')
    for year in range(min_year + 1, max_year):
        start_of_year = Es.year_to_range(year)[0]
        window = datetime.timedelta(1, 12 * 60 * 60)
        for rel in Es.rcol.find({
                'from': {
                    '$gt': start_of_year - window,
                    '$lt': start_of_year + window
                }
        }):
            if rel['from'] == start_of_year:
                continue
            how = Es.by_id(rel['how'])
            print(' {} {} (as {}): {} -> {}'.format(
                six.text_type(Es.by_id(rel['who'])),
                str(Es.by_id(rel['with'])),
                how._data['sofa_suffix'] if how else 'member', rel['from'],
                start_of_year))
            if args.apply:
                rel['from'] = start_of_year
                Es.rcol.save(rel)

    print('Any relation that ends near the change of year, should end')
    print('exactly on the change of year ...')
    for year in range(min_year + 1, max_year):
        start_of_year = Es.year_to_range(year)[0]
        end_of_year = Es.year_to_range(year)[0] - datetime.timedelta(0, 1)
        window = datetime.timedelta(1, 12 * 60 * 60)
        for rel in Es.rcol.find({
                'until': {
                    '$gt': start_of_year - window,
                    '$lt': start_of_year + window
                }
        }):
            if rel['until'] == end_of_year:
                continue
            how = Es.by_id(rel['how'])
            print(' {} {} (as {}): {} -> {}'.format(
                six.text_type(Es.by_id(rel['who'])),
                str(Es.by_id(rel['with'])),
                how._data['sofa_suffix'] if how else 'member', rel['until'],
                end_of_year))
            if args.apply:
                rel['until'] = end_of_year
                Es.rcol.save(rel)

    print('End eerstejaars relations from previous year ...')
    ej = Es.by_name('eerstejaars')
    members = Es.rcol.find({'with': ej._data['_id'], 'until': Es.DT_MAX})
    for r in sorted(members, key=lambda r: r['from']):
        member = Es.by_id(r['who'])
        if r['from'] >= datetime.datetime(today.year, 7, 1):
            continue  # Only just became a member, don't remove.
        until = datetime.datetime(today.year, 8, 31, 23, 59, 59)
        print(' {:25} from {:26} until None -> {}'.format(
            member.humanName, str(r['from']), str(until)))
        if args.apply:
            r['until'] = until
            Es.rcol.save(r)