Ejemplo n.º 1
0
def relation_end(request, _id):
    rel = Es.relation_by_id(_id)
    if rel is None:
        raise Http404
    if not Es.user_may_end_relation(request.user, rel):
        raise PermissionDenied
    Es.end_relation(_id)
    giedo.sync()
    return redirect_to_referer(request)
Ejemplo n.º 2
0
def _entity_detail(request, e):
    def _cmp(x,y):
        r = Es.relation_cmp_until(y,x)
        if r: return r
        r = cmp(unicode(x['with'].humanName),
                unicode(y['with'].humanName))
        if r: return r
        r = cmp(unicode(x['how'].humanName) if x['how'] else None,
            unicode(y['how'].humanName) if y['how'] else None)
        if r: return r
        return Es.relation_cmp_from(x,y)
    def _rcmp(x,y):
        r = Es.relation_cmp_until(y,x)
        if r: return r
        r = cmp(unicode(x['how'].humanName) if x['how'] else None,
            unicode(y['how'].humanName) if y['how'] else None)
        if r: return r
        r = cmp(unicode(x['who'].humanName),
                unicode(y['who'].humanName))
        if r: return r
        return Es.relation_cmp_from(x,y)
    related = sorted(e.get_related(), cmp=_cmp)
    rrelated = sorted(e.get_rrelated(), cmp=_rcmp)
    for r in chain(related, rrelated):
        r['may_end'] = Es.user_may_end_relation(request.user, r)
        r['id'] = r['_id']
        r['until_year'] = (None if r['until'] is None else
                    Es.date_to_year(r['until']))
        r['virtual'] = Es.relation_is_virtual(r)
    tags = [t.as_primary_type() for t in e.get_tags()]
    ctx = {'related': related,
           'rrelated': rrelated,
           'now': now(),
           'tags': sorted(tags, Es.entity_cmp_humanName),
           'object': e}
    # Is request.user allowed to add (r)relations?
    if ('secretariaat' in request.user.cached_groups_names
            and (e.is_group or e.is_user)):
        groups = [g for g in Es.groups() if not g.is_virtual]
        groups.sort(cmp=lambda x,y: cmp(unicode(x.humanName),
                        unicode(y.humanName)))
        users = sorted(Es.users(), cmp=Es.entity_cmp_humanName)
        brands = sorted(Es.brands(), cmp=Es.entity_cmp_humanName)
        ctx.update({'users': users,
                'brands': brands,
                'groups': groups,
                'may_add_related': True,
                'may_add_rrelated': True})
    if e.is_tag:
        ctx.update({'tag_bearers': sorted(e.as_tag().get_bearers(),
                        cmp=Es.entity_cmp_humanName)})
    return ctx
Ejemplo n.º 3
0
def relation_end(request, _id):
    rel = Es.relation_by_id(_id)
    if rel is None:
        raise Http404
    if not Es.user_may_end_relation(request.user, rel):
        raise PermissionDenied
    Es.end_relation(_id)

    # Notify informacie
    # TODO (rik) leave out 'als lid'
    Es.notify_informacie('relation_end', request.user, relation=_id)

    giedo.sync_async(request)
    return redirect_to_referer(request)
Ejemplo n.º 4
0
def relation_end(request, _id):
    rel = Es.relation_by_id(_id)
    if rel is None:
        raise Http404
    if not Es.relation_is_active(rel):
        messages.info(request, _("Relatie was al beëindigd."))
        return redirect_to_referer(request)
    if not Es.user_may_end_relation(request.user, rel):
        raise PermissionDenied
    Es.end_relation(_id)

    # Notify informacie
    # TODO (rik) leave out 'als lid'
    Es.notify_informacie('relation_end', request.user, relation=_id)

    giedo.sync_async(request)
    return redirect_to_referer(request)
Ejemplo n.º 5
0
def relation_end(request, _id):
    rel = Es.relation_by_id(_id)
    if rel is None:
        raise Http404
    if not Es.user_may_end_relation(request.user, rel):
        raise PermissionDenied
    Es.end_relation(_id)

    # Notify informacie
    if request.user == rel['who']:
        Es.notify_informacie('relation_ended', relation=_id)
    else:
        # TODO (rik) leave out 'als lid'
        Es.notify_informacie('relation_end', relation=_id)

    giedo.sync_async(request)
    return redirect_to_referer(request)
Ejemplo n.º 6
0
def relation_end(request, _id):
    rel = Es.relation_by_id(_id)
    if rel is None:
        raise Http404
    if not Es.user_may_end_relation(request.user, rel):
        raise PermissionDenied
    Es.end_relation(_id)
    # Notify informacie
    if request.user == rel['who']:
        Es.notify_informacie("%s heeft zich uitgeschreven als %s %s" % (
                        request.user.full_name,
                        rel['how'].humanName if rel['how'] else 'lid',
                        rel['with'].humanName.genitive))
    else:
        # TODO (rik) leave out 'als lid'
        Es.notify_informacie("%s is geen %s meer %s" % (
                        rel['who'].humanName,
                        rel['how'].humanName if rel['how'] else 'lid',
                        rel['with'].humanName.genitive))
    giedo.sync_async(request)
    return redirect_to_referer(request)
Ejemplo n.º 7
0
def _entity_detail(request, e):
    related = sorted(
        e.get_related(),
        key=lambda x: (Es.DT_MIN - Es.relation_until(
            x), Es.entity_humanName(x['with']), Es.entity_humanName(x['how'])))
    rrelated = sorted(
        e.get_rrelated(),
        key=lambda x: (Es.DT_MIN - Es.relation_until(
            x), Es.entity_humanName(x['how']), Es.entity_humanName(x['who'])))
    for r in chain(related, rrelated):
        r['may_end'] = Es.user_may_end_relation(request.user, r)
        r['id'] = r['_id']
        r['until_year'] = (None if r['until'] is None or r['until'] >= now()
                           else Es.date_to_year(r['until']))
        r['virtual'] = Es.relation_is_virtual(r)
    tags = [t.as_primary_type() for t in e.get_tags()]

    # mapping of year => set of members
    year_sets = {}
    for r in rrelated:
        year = r['until_year']
        if year is None:
            year = 'this'

        if year not in year_sets:
            year_sets[year] = set()
        year_sets[year].add(r['who'])

    year_counts = {}
    for year in year_sets:
        year_counts[year] = len(year_sets[year])

    ctx = {
        'related': related,
        'rrelated': rrelated,
        'year_counts': year_counts,
        'now': now(),
        'tags': sorted(tags, key=Es.entity_humanName),
        'object': e,
        'chiefs': [],
        'pipos': [],
        'reps': []
    }
    for r in rrelated:
        if r['how'] and Es.relation_is_active(r):
            if str(r['how'].name) == '!brand-hoofd':
                r['hidden'] = True
                ctx['chiefs'].append(r)
            if str(r['how'].name) == '!brand-bestuurspipo':
                r['hidden'] = True
                ctx['pipos'].append(r)
            if str(r['how'].name) == '!brand-vertegenwoordiger':
                r['hidden'] = True
                ctx['reps'].append(r)
    # Is request.user allowed to add (r)relations?
    if ('secretariaat' in request.user.cached_groups_names
            and (e.is_group or e.is_user)):
        groups = [g for g in Es.groups() if not g.is_virtual]
        groups.sort(key=Es.entity_humanName)
        users = sorted(Es.users(), key=Es.entity_humanName)
        brands = sorted(Es.brands(), key=Es.entity_humanName)
        ctx.update({
            'users': users,
            'brands': brands,
            'groups': groups,
            'may_add_related': True,
            'may_add_rrelated': True,
            'may_tag': True,
            'may_untag': True
        })
    ctx['may_upload_smoel'] = e.name and request.user.may_upload_smoel_for(e)
    if e.is_tag:
        ctx.update({
            'tag_bearers':
            sorted(e.as_tag().get_bearers(), key=Es.entity_humanName)
        })

    # Check whether entity has a photo
    photo_size = e.photo_size
    if e.photo_size is not None:
        ctx.update({
            'hasPhoto': True,
            'photoWidth': photo_size[0],
            'photoHeight': photo_size[1]
        })
    return ctx
Ejemplo n.º 8
0
def _entity_detail(request, e):
    def _cmp(x,y):
        r = Es.relation_cmp_until(y,x)
        if r: return r
        r = cmp(unicode(x['with'].humanName),
                unicode(y['with'].humanName))
        if r: return r
        r = cmp(unicode(x['how'].humanName) if x['how'] else None,
            unicode(y['how'].humanName) if y['how'] else None)
        if r: return r
        return Es.relation_cmp_from(x,y)
    def _rcmp(x,y):
        r = Es.relation_cmp_until(y,x)
        if r: return r
        r = cmp(unicode(x['how'].humanName) if x['how'] else None,
            unicode(y['how'].humanName) if y['how'] else None)
        if r: return r
        r = cmp(unicode(x['who'].humanName),
                unicode(y['who'].humanName))
        if r: return r
        return Es.relation_cmp_from(x,y)
    related = sorted(e.get_related(), cmp=_cmp)
    rrelated = sorted(e.get_rrelated(), cmp=_rcmp)
    for r in chain(related, rrelated):
        r['may_end'] = Es.user_may_end_relation(request.user, r)
        r['id'] = r['_id']
        r['until_year'] = (None if r['until'] is None else
                    Es.date_to_year(r['until']))
        r['virtual'] = Es.relation_is_virtual(r)
    tags = [t.as_primary_type() for t in e.get_tags()]

    # mapping of year => set of members
    year_sets = {}
    for r in rrelated:
        year = r['until_year']
        if year is None:
            year = 'this'

        if not year in year_sets:
            year_sets[year] = set()
        year_sets[year].add(r['who'])

    year_counts = {}
    for year in year_sets:
        year_counts[year] = len(year_sets[year])

    ctx = {'related': related,
           'rrelated': rrelated,
           'year_counts': year_counts,
           'now': now(),
           'tags': sorted(tags, Es.entity_cmp_humanName),
           'object': e,
           'chiefs': [],
           'pipos': [] }
    for r in rrelated:
        if r['how'] and Es.relation_is_active(r):
            if str(r['how'].name) == '!brand-hoofd':
                r['hidden'] = True
                ctx['chiefs'].append(r)
            if str(r['how'].name) == '!brand-bestuurspipo':
                r['hidden'] = True
                ctx['pipos'].append(r)
    # Is request.user allowed to add (r)relations?
    if ('secretariaat' in request.user.cached_groups_names
            and (e.is_group or e.is_user)):
        groups = [g for g in Es.groups() if not g.is_virtual]
        groups.sort(cmp=lambda x,y: cmp(unicode(x.humanName),
                        unicode(y.humanName)))
        users = sorted(Es.users(), cmp=Es.entity_cmp_humanName)
        brands = sorted(Es.brands(), cmp=Es.entity_cmp_humanName)
        ctx.update({'users': users,
                'brands': brands,
                'groups': groups,
                'may_add_related': True,
                'may_add_rrelated': True})
    if e.is_tag:
        ctx.update({'tag_bearers': sorted(e.as_tag().get_bearers(),
                        cmp=Es.entity_cmp_humanName)})
    return ctx
Ejemplo n.º 9
0
def _entity_detail(request, e):
    related = sorted(e.get_related(),
                     key=lambda x: (Es.DT_MIN - Es.relation_until(x),
                                    Es.entity_humanName(x['with']),
                                    Es.entity_humanName(x['how'])))
    rrelated = sorted(e.get_rrelated(),
                      key=lambda x: (Es.DT_MIN - Es.relation_until(x),
                                     Es.entity_humanName(x['how']),
                                     Es.entity_humanName(x['who'])))
    for r in chain(related, rrelated):
        r['may_end'] = Es.user_may_end_relation(request.user, r)
        r['id'] = r['_id']
        r['until_year'] = (None if r['until'] is None
                           or r['until'] >= now()
                           else Es.date_to_year(r['until']))
        r['virtual'] = Es.relation_is_virtual(r)
    tags = [t.as_primary_type() for t in e.get_tags()]

    # mapping of year => set of members
    year_sets = {}
    for r in rrelated:
        year = r['until_year']
        if year is None:
            year = 'this'

        if year not in year_sets:
            year_sets[year] = set()
        year_sets[year].add(r['who'])

    year_counts = {}
    for year in year_sets:
        year_counts[year] = len(year_sets[year])

    ctx = {'related': related,
           'rrelated': rrelated,
           'year_counts': year_counts,
           'now': now(),
           'tags': sorted(tags, key=Es.entity_humanName),
           'object': e,
           'chiefs': [],
           'pipos': [],
           'reps': []}
    for r in rrelated:
        if r['how'] and Es.relation_is_active(r):
            if str(r['how'].name) == '!brand-hoofd':
                r['hidden'] = True
                ctx['chiefs'].append(r)
            if str(r['how'].name) == '!brand-bestuurspipo':
                r['hidden'] = True
                ctx['pipos'].append(r)
            if str(r['how'].name) == '!brand-vertegenwoordiger':
                r['hidden'] = True
                ctx['reps'].append(r)
    # Is request.user allowed to add (r)relations?
    if ('secretariaat' in request.user.cached_groups_names
            and (e.is_group or e.is_user)):
        groups = [g for g in Es.groups() if not g.is_virtual]
        groups.sort(key=Es.entity_humanName)
        users = sorted(Es.users(), key=Es.entity_humanName)
        brands = sorted(Es.brands(), key=Es.entity_humanName)
        ctx.update({'users': users,
                    'brands': brands,
                    'groups': groups,
                    'may_add_related': True,
                    'may_add_rrelated': True,
                    'may_tag': True,
                    'may_untag': True})
    ctx['may_upload_smoel'] = e.name and request.user.may_upload_smoel_for(e)
    if e.is_tag:
        ctx.update({'tag_bearers': sorted(e.as_tag().get_bearers(),
                                          key=Es.entity_humanName)})

    # Check whether entity has a photo
    photos_path = (path.join(settings.SMOELEN_PHOTOS_PATH, str(e.name))
                   if e.name else None)
    if photos_path and default_storage.exists(photos_path + '.jpg'):
        img = PIL.Image.open(default_storage.open(photos_path + '.jpg'))
        width, height = img.size
        if default_storage.exists(photos_path + '.orig'):
            # smoel was created using newer strategy. Shrink until it fits the
            # requirements.
            width, height = resize_proportional(img.size[0], img.size[1],
                                                settings.SMOELEN_WIDTH,
                                                settings.SMOELEN_HEIGHT)
        elif width > settings.SMOELEN_WIDTH:
            # smoel was created as high-resolution image, probably 600px wide
            width /= 2
            height /= 2
        else:
            # smoel was created as normal image, probably 300px wide
            pass
        ctx.update({
            'hasPhoto': True,
            'photoWidth': width,
            'photoHeight': height})
    return ctx
Ejemplo n.º 10
0
def _entity_detail(request, e):
    def _cmp(x,y):
        r = Es.relation_cmp_until(y,x)
        if r: return r
        r = cmp(unicode(x['with'].humanName),
                unicode(y['with'].humanName))
        if r: return r
        r = cmp(unicode(x['how'].humanName) if x['how'] else None,
            unicode(y['how'].humanName) if y['how'] else None)
        if r: return r
        return Es.relation_cmp_from(x,y)
    def _rcmp(x,y):
        r = Es.relation_cmp_until(y,x)
        if r: return r
        r = cmp(unicode(x['how'].humanName) if x['how'] else None,
            unicode(y['how'].humanName) if y['how'] else None)
        if r: return r
        r = cmp(unicode(x['who'].humanName),
                unicode(y['who'].humanName))
        if r: return r
        return Es.relation_cmp_from(x,y)
    related = sorted(e.get_related(), cmp=_cmp)
    rrelated = sorted(e.get_rrelated(), cmp=_rcmp)
    for r in chain(related, rrelated):
        r['may_end'] = Es.user_may_end_relation(request.user, r)
        r['id'] = r['_id']
        r['until_year'] = (None if r['until'] is None else
                    Es.date_to_year(r['until']))
        r['virtual'] = Es.relation_is_virtual(r)
    tags = [t.as_primary_type() for t in e.get_tags()]

    # mapping of year => set of members
    year_sets = {}
    for r in rrelated:
        year = r['until_year']
        if year is None:
            year = 'this'

        if not year in year_sets:
            year_sets[year] = set()
        year_sets[year].add(r['who'])

    year_counts = {}
    for year in year_sets:
        year_counts[year] = len(year_sets[year])

    ctx = {'related': related,
           'rrelated': rrelated,
           'year_counts': year_counts,
           'now': now(),
           'tags': sorted(tags, Es.entity_cmp_humanName),
           'object': e,
           'chiefs': [],
           'pipos': [] }
    for r in rrelated:
        if r['how'] and Es.relation_is_active(r):
            if str(r['how'].name) == '!brand-hoofd':
                r['hidden'] = True
                ctx['chiefs'].append(r)
            if str(r['how'].name) == '!brand-bestuurspipo':
                r['hidden'] = True
                ctx['pipos'].append(r)
    # Is request.user allowed to add (r)relations?
    if ('secretariaat' in request.user.cached_groups_names
            and (e.is_group or e.is_user)):
        groups = [g for g in Es.groups() if not g.is_virtual]
        groups.sort(cmp=lambda x,y: cmp(unicode(x.humanName),
                        unicode(y.humanName)))
        users = sorted(Es.users(), cmp=Es.entity_cmp_humanName)
        brands = sorted(Es.brands(), cmp=Es.entity_cmp_humanName)
        ctx.update({'users': users,
                'brands': brands,
                'groups': groups,
                'may_add_related': True,
                'may_add_rrelated': True})
    if e.is_tag:
        ctx.update({'tag_bearers': sorted(e.as_tag().get_bearers(),
                        cmp=Es.entity_cmp_humanName)})
    return ctx
Ejemplo n.º 11
0
def _entity_detail(request, e):
    def _cmp(x, y):
        r = Es.relation_cmp_until(y, x)
        if r:
            return r
        r = cmp(unicode(x["with"].humanName), unicode(y["with"].humanName))
        if r:
            return r
        r = cmp(unicode(x["how"].humanName) if x["how"] else None, unicode(y["how"].humanName) if y["how"] else None)
        if r:
            return r
        return Es.relation_cmp_from(x, y)

    def _rcmp(x, y):
        r = Es.relation_cmp_until(y, x)
        if r:
            return r
        r = cmp(unicode(x["how"].humanName) if x["how"] else None, unicode(y["how"].humanName) if y["how"] else None)
        if r:
            return r
        r = cmp(unicode(x["who"].humanName), unicode(y["who"].humanName))
        if r:
            return r
        return Es.relation_cmp_from(x, y)

    related = sorted(e.get_related(), cmp=_cmp)
    rrelated = sorted(e.get_rrelated(), cmp=_rcmp)
    for r in chain(related, rrelated):
        r["may_end"] = Es.user_may_end_relation(request.user, r)
        r["id"] = r["_id"]
        r["until_year"] = None if r["until"] is None or r["until"] >= now() else Es.date_to_year(r["until"])
        r["virtual"] = Es.relation_is_virtual(r)
    tags = [t.as_primary_type() for t in e.get_tags()]

    # mapping of year => set of members
    year_sets = {}
    for r in rrelated:
        year = r["until_year"]
        if year is None:
            year = "this"

        if not year in year_sets:
            year_sets[year] = set()
        year_sets[year].add(r["who"])

    year_counts = {}
    for year in year_sets:
        year_counts[year] = len(year_sets[year])

    ctx = {
        "related": related,
        "rrelated": rrelated,
        "year_counts": year_counts,
        "now": now(),
        "tags": sorted(tags, Es.entity_cmp_humanName),
        "object": e,
        "chiefs": [],
        "pipos": [],
        "reps": [],
    }
    for r in rrelated:
        if r["how"] and Es.relation_is_active(r):
            if str(r["how"].name) == "!brand-hoofd":
                r["hidden"] = True
                ctx["chiefs"].append(r)
            if str(r["how"].name) == "!brand-bestuurspipo":
                r["hidden"] = True
                ctx["pipos"].append(r)
            if str(r["how"].name) == "!brand-vertegenwoordiger":
                r["hidden"] = True
                ctx["reps"].append(r)
    # Is request.user allowed to add (r)relations?
    if "secretariaat" in request.user.cached_groups_names and (e.is_group or e.is_user):
        groups = [g for g in Es.groups() if not g.is_virtual]
        groups.sort(cmp=lambda x, y: cmp(unicode(x.humanName), unicode(y.humanName)))
        users = sorted(Es.users(), cmp=Es.entity_cmp_humanName)
        brands = sorted(Es.brands(), cmp=Es.entity_cmp_humanName)
        ctx.update(
            {
                "users": users,
                "brands": brands,
                "groups": groups,
                "may_add_related": True,
                "may_add_rrelated": True,
                "may_tag": True,
                "may_untag": True,
            }
        )
    ctx["may_upload_smoel"] = e.name and request.user.may_upload_smoel_for(e)
    if e.is_tag:
        ctx.update({"tag_bearers": sorted(e.as_tag().get_bearers(), cmp=Es.entity_cmp_humanName)})

    # Check whether entity has a photo
    photos_path = path.join(settings.SMOELEN_PHOTOS_PATH, str(e.name)) if e.name else None
    if photos_path and default_storage.exists(photos_path + ".jpg"):
        img = Image.open(default_storage.open(photos_path + ".jpg"))
        width, height = img.size
        if default_storage.exists(photos_path + ".orig"):
            # smoel was created using newer strategy. Shrink until it fits the
            # requirements.
            width, height = resize_proportional(
                img.size[0], img.size[1], settings.SMOELEN_WIDTH, settings.SMOELEN_HEIGHT
            )
        elif width > settings.SMOELEN_WIDTH:
            # smoel was created as high-resolution image, probably 600px wide
            width /= 2
            height /= 2
        else:
            # smoel was created as normal image, probably 300px wide
            pass
        ctx.update({"hasPhoto": True, "photoWidth": width, "photoHeight": height})
    return ctx
Ejemplo n.º 12
0
def _entity_detail(request, e):
    related = sorted(e.get_related(),
                     key=lambda x: (Es.DT_MIN - Es.relation_until(x),
                                    Es.entity_humanName(x['with']),
                                    Es.entity_humanName(x['how'])))
    rrelated = sorted(e.get_rrelated(),
                      key=lambda x: (Es.DT_MIN - Es.relation_until(x),
                                     Es.entity_humanName(x['how']),
                                     Es.entity_humanName(x['who'])))
    for r in chain(related, rrelated):
        r['may_end'] = Es.user_may_end_relation(request.user, r)
        r['id'] = r['_id']
        r['until_year'] = (None if r['until'] is None
                           or r['until'] >= now()
                           else Es.date_to_year(r['until']))
        r['virtual'] = Es.relation_is_virtual(r)
    tags = [t.as_primary_type() for t in e.get_tags()]

    # mapping of year => set of members
    year_sets = {}
    for r in rrelated:
        year = r['until_year']
        if year is None:
            year = 'this'

        if year not in year_sets:
            year_sets[year] = set()
        year_sets[year].add(r['who'])

    year_counts = {}
    for year in year_sets:
        year_counts[year] = len(year_sets[year])

    ctx = {'related': related,
           'rrelated': rrelated,
           'year_counts': year_counts,
           'now': now(),
           'tags': sorted(tags, key=Es.entity_humanName),
           'object': e,
           'chiefs': [],
           'pipos': [],
           'reps': []}
    for r in rrelated:
        if r['how'] and Es.relation_is_active(r):
            if str(r['how'].name) == '!brand-hoofd':
                r['hidden'] = True
                ctx['chiefs'].append(r)
            if str(r['how'].name) == '!brand-bestuurspipo':
                r['hidden'] = True
                ctx['pipos'].append(r)
            if str(r['how'].name) == '!brand-vertegenwoordiger':
                r['hidden'] = True
                ctx['reps'].append(r)
    # Is request.user allowed to add (r)relations?
    if ('secretariaat' in request.user.cached_groups_names
            and (e.is_group or e.is_user)):
        groups = [g for g in Es.groups() if not g.is_virtual]
        groups.sort(key=Es.entity_humanName)
        users = sorted(Es.users(), key=Es.entity_humanName)
        brands = sorted(Es.brands(), key=Es.entity_humanName)
        ctx.update({'users': users,
                    'brands': brands,
                    'groups': groups,
                    'may_add_related': True,
                    'may_add_rrelated': True,
                    'may_tag': True,
                    'may_untag': True})
    ctx['may_upload_smoel'] = e.name and request.user.may_upload_smoel_for(e)
    if e.is_tag:
        ctx.update({'tag_bearers': sorted(e.as_tag().get_bearers(),
                                          key=Es.entity_humanName)})

    # Check whether entity has a photo
    photo_size = e.photo_size
    if e.photo_size is not None:
        ctx.update({
            'hasPhoto': True,
            'photoWidth': photo_size[0],
            'photoHeight': photo_size[1]})
    return ctx