def edit_profile(request):
    ''' View for editing an attendee's profile

    The user must be logged in to edit their profile.

    Returns:
        redirect or render:
            In the case of a ``POST`` request, it'll redirect to ``dashboard``,
            or otherwise, it will render ``registrasion/profile_form.html``
            with data::

                {
                    "form": form,  # Instance of ATTENDEE_PROFILE_FORM.
                }

    '''

    form, handled = _handle_profile(request, "profile")

    if handled and not form.errors:
        messages.success(
            request,
            "Your attendee profile was updated.",
        )
        return redirect("dashboard")

    data = {
        "form": form,
    }
    return render(request, "registrasion/profile_form.html", data)

def
edit_profile
(
request
)
:
form
,
handled
=
_handle_profile
(
request
,
"profile"
)
if
handled
and
not
form
.
errors
:
messages
.
success
(
request
,
"Your attendee profile was updated."
,
)
return
redirect
(
"dashboard"
)
data
=
{
"form"
:
form
,
}
return
render
(
request
,
"registrasion/profile_form.html"
,
data
)


def _handle_profile(request, prefix):
    ''' Returns a profile form instance, and a boolean which is true if the
    form was handled. '''
    attendee = people.Attendee.get_instance(request.user)

    try:
        profile = attendee.attendeeprofilebase
        profile = people.AttendeeProfileBase.objects.get_subclass(
            pk=profile.id,
        )
    except ObjectDoesNotExist:
        profile = None

    # Load a pre-entered name from the speaker's profile,
    # if they have one.
    try:
        speaker_profile = request.user.speaker_profile
        speaker_name = speaker_profile.name
    except ObjectDoesNotExist:
        speaker_name = None

    name_field = ProfileForm.Meta.model.name_field()
    initial = {}
    if profile is None and name_field is not None:
        initial[name_field] = speaker_name

    form = ProfileForm(
        request.POST or None,
        initial=initial,
        instance=profile,
        prefix=prefix
    )

    handled = True if request.POST else False

    if request.POST and form.is_valid():
        form.instance.attendee = attendee
        form.save()

    return form, handled

def _handle_profile ( request , prefix ) : attendee = people . Attendee . get_instance ( request . user ) try : profile = attendee . attendeeprofilebase profile = people . AttendeeProfileBase . objects . get_subclass ( pk = profile . id , ) except ObjectDoesNotExist : profile = None try : speaker_profile = request . user . speaker_profile speaker_name = speaker_profile . name except ObjectDoesNotExist : speaker_name = None name_field = ProfileForm . Meta . model . name_field ( ) initial = { } if profile is None and name_field is not None : initial [ name_field ] = speaker_name form = ProfileForm ( request . POST or None , initial = initial , instance = profile , prefix = prefix ) handled = True if request . POST else False if request . POST and form . is_valid ( ) : form . instance . attendee = attendee form . save ( ) return form , handled

Returns a profile form instance and a boolean which is true if the form was handled .


def product_category(request, category_id):
    ''' Form for selecting products from an individual product category.

    Arguments:
        category_id (castable to int): The id of the category to display.

    Returns:
        redirect or render:
            If the form has been sucessfully submitted, redirect to
            ``dashboard``. Otherwise, render
            ``registrasion/product_category.html`` with data::

                {
                    "category": category,         # An inventory.Category for
                                                  # category_id
                    "discounts": discounts,       # A list of
                                                  # DiscountAndQuantity
                    "form": products_form,        # A form for selecting
                                                  # products
                    "voucher_form": voucher_form, # A form for entering a
                                                  # voucher code
                }

    '''

    PRODUCTS_FORM_PREFIX = "products"
    VOUCHERS_FORM_PREFIX = "vouchers"

    # Handle the voucher form *before* listing products.
    # Products can change as vouchers are entered.
    v = _handle_voucher(request, VOUCHERS_FORM_PREFIX)
    voucher_form, voucher_handled = v

    category_id = int(category_id)  # Routing is [0-9]+
    category = inventory.Category.objects.get(pk=category_id)

    with BatchController.batch(request.user):
        products = ProductController.available_products(
            request.user,
            category=category,
        )

        if not products:
            messages.warning(
                request,
                (
                    "There are no products available from category: " +
                    category.name
                ),
            )
            return redirect("dashboard")

        p = _handle_products(request, category, products, PRODUCTS_FORM_PREFIX)
        products_form, discounts, products_handled = p

    if request.POST and not voucher_handled and not products_form.errors:
        # Only return to the dashboard if we didn't add a voucher code
        # and if there's no errors in the products form
        if products_form.has_changed():
            messages.success(
                request,
                "Your reservations have been updated.",
            )
        return redirect(review)

    data = {
        "category": category,
        "discounts": discounts,
        "form": products_form,
        "voucher_form": voucher_form,
    }

    return render(request, "registrasion/product_category.html", data)

Form for selecting products from an individual product category .


def _handle_products(request, category, products, prefix):
    ''' Handles a products list form in the given request. Returns the
    form instance, the discounts applicable to this form, and whether the
    contents were handled. '''

    current_cart = CartController.for_user(request.user)

    ProductsForm = forms.ProductsForm(category, products)

    # Create initial data for each of products in category
    items = commerce.ProductItem.objects.filter(
        product__in=products,
        cart=current_cart.cart,
    ).select_related("product")
    quantities = []
    seen = set()

    for item in items:
        quantities.append((item.product, item.quantity))
        seen.add(item.product)

    zeros = set(products) - seen
    for product in zeros:
        quantities.append((product, 0))

    products_form = ProductsForm(
        request.POST or None,
        product_quantities=quantities,
        prefix=prefix,
    )

    if request.method == "POST" and products_form.is_valid():
        if products_form.has_changed():
            _set_quantities_from_products_form(products_form, current_cart)

        # If category is required, the user must have at least one
        # in an active+valid cart
        if category.required:
            carts = commerce.Cart.objects.filter(user=request.user)
            items = commerce.ProductItem.objects.filter(
                product__category=category,
                cart=carts,
            )
            if len(items) == 0:
                products_form.add_error(
                    None,
                    "You must have at least one item from this category",
                )
    handled = False if products_form.errors else True

    # Making this a function to lazily evaluate when it's displayed
    # in templates.

    discounts = util.lazy(
        DiscountController.available_discounts,
        request.user,
        [],
        products,
    )

    return products_form, discounts, handled

Handles a products list form in the given request . Returns the form instance the discounts applicable to this form and whether the contents were handled .


def _handle_voucher(request, prefix):
    ''' Handles a voucher form in the given request. Returns the voucher
    form instance, and whether the voucher code was handled. '''

    voucher_form = forms.VoucherForm(request.POST or None, prefix=prefix)
    current_cart = CartController.for_user(request.user)

    if (voucher_form.is_valid() and
            voucher_form.cleaned_data["voucher"].strip()):

        voucher = voucher_form.cleaned_data["voucher"]
        voucher = inventory.Voucher.normalise_code(voucher)

        if len(current_cart.cart.vouchers.filter(code=voucher)) > 0:
            # This voucher has already been applied to this cart.
            # Do not apply code
            handled = False
        else:
            try:
                current_cart.apply_voucher(voucher)
            except Exception as e:
                voucher_form.add_error("voucher", e)
            handled = True
    else:
        handled = False

    return (voucher_form, handled)

def _handle_voucher ( request , prefix ) : voucher_form = forms . VoucherForm ( request . POST or None , prefix = prefix ) current_cart = CartController . for_user ( request . user ) if ( voucher_form . is_valid ( ) and voucher_form . cleaned_data [ "voucher" ] . strip ( ) ) : voucher = voucher_form . cleaned_data [ "voucher" ] voucher = inventory . Voucher . normalise_code ( voucher ) if len ( current_cart . cart . vouchers . filter ( code = voucher ) ) > 0 : handled = False else : try : current_cart . apply_voucher ( voucher ) except Exception as e : voucher_form . add_error ( "voucher" , e ) handled = True else : handled = False return ( voucher_form , handled )

Handles a voucher form in the given request . Returns the voucher form instance and whether the voucher code was handled .


def checkout(request, user_id=None):
    ''' Runs the checkout process for the current cart.

    If the query string contains ``fix_errors=true``, Registrasion will attempt
    to fix errors preventing the system from checking out, including by
    cancelling expired discounts and vouchers, and removing any unavailable
    products.

    Arguments:
        user_id (castable to int):
            If the requesting user is staff, then the user ID can be used to
            run checkout for another user.
    Returns:
        render or redirect:
            If the invoice is generated successfully, or there's already a
            valid invoice for the current cart, redirect to ``invoice``.
            If there are errors when generating the invoice, render
            ``registrasion/checkout_errors.html`` with the following data::

                {
                    "error_list", [str, ...]  # The errors to display.
                }

    '''

    if user_id is not None:
        if request.user.is_staff:
            user = User.objects.get(id=int(user_id))
        else:
            raise Http404()
    else:
        user = request.user

    current_cart = CartController.for_user(user)

    if "fix_errors" in request.GET and request.GET["fix_errors"] == "true":
        current_cart.fix_simple_errors()

    try:
        current_invoice = InvoiceController.for_cart(current_cart.cart)
    except ValidationError as ve:
        return _checkout_errors(request, ve)

    return redirect("invoice", current_invoice.invoice.id)

def checkout ( request , user_id = None ) : if user_id is not None : if request . user . is_staff : user = User . objects . get ( id = int ( user_id ) ) else : raise Http404 ( ) else : user = request . user current_cart = CartController . for_user ( user ) if "fix_errors" in request . GET and request . GET [ "fix_errors" ] == "true" : current_cart . fix_simple_errors ( ) try : current_invoice = InvoiceController . for_cart ( current_cart . cart ) except ValidationError as ve : return _checkout_errors ( request , ve ) return redirect ( "invoice" , current_invoice . invoice . id )

Runs the checkout process for the current cart .


def invoice_access(request, access_code):
    ''' Redirects to an invoice for the attendee that matches the given access
    code, if any.

    If the attendee has multiple invoices, we use the following tie-break:

    - If there's an unpaid invoice, show that, otherwise
    - If there's a paid invoice, show the most recent one, otherwise
    - Show the most recent invoid of all

    Arguments:

        access_code (castable to int): The access code for the user whose
            invoice you want to see.

    Returns:
        redirect:
            Redirect to the selected invoice for that user.

    Raises:
        Http404: If the user has no invoices.
    '''

    invoices = commerce.Invoice.objects.filter(
        user__attendee__access_code=access_code,
    ).order_by("-issue_time")

    if not invoices:
        raise Http404()

    unpaid = invoices.filter(status=commerce.Invoice.STATUS_UNPAID)
    paid = invoices.filter(status=commerce.Invoice.STATUS_PAID)

    if unpaid:
        invoice = unpaid[0]  # (should only be 1 unpaid invoice?)
    elif paid:
        invoice = paid[0]  # Most recent paid invoice
    else:
        invoice = invoices[0]  # Most recent of any invoices

    return redirect("invoice", invoice.id, access_code)

def invoice_access ( request , access_code ) : invoices = commerce . Invoice . objects . filter ( user__attendee__access_code = access_code , ) . order_by ( "-issue_time" ) if not invoices : raise Http404 ( ) unpaid = invoices . filter ( status = commerce . Invoice . STATUS_UNPAID ) paid = invoices . filter ( status = commerce . Invoice . STATUS_PAID ) if unpaid : invoice = unpaid [ 0 ] elif paid : invoice = paid [ 0 ] else : invoice = invoices [ 0 ] return redirect ( "invoice" , invoice . id , access_code )

Redirects to an invoice for the attendee that matches the given access code if any .


def invoice(request, invoice_id, access_code=None):
    ''' Displays an invoice.

    This view is not authenticated, but it will only allow access to either:
    the user the invoice belongs to; staff; or a request made with the correct
    access code.

    Arguments:

        invoice_id (castable to int): The invoice_id for the invoice you want
            to view.

        access_code (Optional[str]): The access code for the user who owns
            this invoice.

    Returns:
        render:
            Renders ``registrasion/invoice.html``, with the following
            data::

                {
                    "invoice": models.commerce.Invoice(),
                }

    Raises:
        Http404: if the current user cannot view this invoice and the correct
            access_code is not provided.

    '''

    current_invoice = InvoiceController.for_id_or_404(invoice_id)

    if not current_invoice.can_view(
            user=request.user,
            access_code=access_code,
            ):
        raise Http404()

    data = {
        "invoice": current_invoice.invoice,
    }

    return render(request, "registrasion/invoice.html", data)

def invoice ( request , invoice_id , access_code = None ) : current_invoice = InvoiceController . for_id_or_404 ( invoice_id ) if not current_invoice . can_view ( user = request . user , access_code = access_code , ) : raise Http404 ( ) data = { "invoice" : current_invoice . invoice , } return render ( request , "registrasion/invoice.html" , data )


def manual_payment(request, invoice_id):
    ''' Allows staff to make manual payments or refunds on an invoice.

    This form requires a login, and the logged in user needs to be staff.

    Arguments:
        invoice_id (castable to int): The invoice ID to be paid

    Returns:
        render:
            Renders ``registrasion/manual_payment.html`` with the following
            data::

                {
                    "invoice": models.commerce.Invoice(),
                    "form": form,   # A form that saves a ``ManualPayment``
                                    # object.
                }

    '''

    FORM_PREFIX = "manual_payment"

    current_invoice = InvoiceController.for_id_or_404(invoice_id)

    form = forms.ManualPaymentForm(
        request.POST or None,
        prefix=FORM_PREFIX,
    )

    if request.POST and form.is_valid():
        form.instance.invoice = current_invoice.invoice
        form.instance.entered_by = request.user
        form.save()
        current_invoice.update_status()
        form = forms.ManualPaymentForm(prefix=FORM_PREFIX)

    data = {
        "invoice": current_invoice.invoice,
        "form": form,
    }

    return render(request, "registrasion/manual_payment.html", data)

def manual_payment ( request , invoice_id ) : FORM_PREFIX = "manual_payment" current_invoice = InvoiceController . for_id_or_404 ( invoice_id ) form = forms . ManualPaymentForm ( request . POST or None , prefix = FORM_PREFIX , ) if request . POST and form . is_valid ( ) : form . instance . invoice = current_invoice . invoice form . instance . entered_by = request . user form . save ( ) current_invoice . update_status ( ) form = forms . ManualPaymentForm ( prefix = FORM_PREFIX ) data = { "invoice" : current_invoice . invoice , "form" : form , } return render ( request , "registrasion/manual_payment.html" , data )

Allows staff to make manual payments or refunds on an invoice .


def refund(request, invoice_id):
    ''' Marks an invoice as refunded and requests a credit note for the
    full amount paid against the invoice.

    This view requires a login, and the logged in user must be staff.

    Arguments:
        invoice_id (castable to int): The ID of the invoice to refund.

    Returns:
        redirect:
            Redirects to ``invoice``.

    '''

    current_invoice = InvoiceController.for_id_or_404(invoice_id)

    try:
        current_invoice.refund()
        messages.success(request, "This invoice has been refunded.")
    except ValidationError as ve:
        messages.error(request, ve)

    return redirect("invoice", invoice_id)

def
refund
(
request
,
invoice_id
)
:
current_invoice
=
InvoiceController
.
for_id_or_404
(
invoice_id
)
try
:
current_invoice
.
refund
(
)
messages
.
success
(
request
,
"This invoice has been refunded."
)
except
ValidationError
as
ve
:
messages
.
error
(
request
,
ve
)
return
redirect
(
"invoice"
,
invoice_id
)

Marks an invoice as refunded and requests a credit note for the full amount paid against the invoice .


def credit_note(request, note_id, access_code=None):
    ''' Displays a credit note.

    If ``request`` is a ``POST`` request, forms for applying or refunding
    a credit note will be processed.

    This view requires a login, and the logged in user must be staff.

    Arguments:
        note_id (castable to int): The ID of the credit note to view.

    Returns:
        render or redirect:
            If the "apply to invoice" form is correctly processed, redirect to
            that invoice, otherwise, render ``registration/credit_note.html``
            with the following data::

                {
                    "credit_note": models.commerce.CreditNote(),
                    "apply_form": form,  # A form for applying credit note
                                         # to an invoice.
                    "refund_form": form, # A form for applying a *manual*
                                         # refund of the credit note.
                    "cancellation_fee_form" : form, # A form for generating an
                                                    # invoice with a
                                                    # cancellation fee
                }

    '''

    note_id = int(note_id)
    current_note = CreditNoteController.for_id_or_404(note_id)

    apply_form = forms.ApplyCreditNoteForm(
        current_note.credit_note.invoice.user,
        request.POST or None,
        prefix="apply_note"
    )

    refund_form = forms.ManualCreditNoteRefundForm(
        request.POST or None,
        prefix="refund_note"
    )

    cancellation_fee_form = forms.CancellationFeeForm(
        request.POST or None,
        prefix="cancellation_fee"
    )

    if request.POST and apply_form.is_valid():
        inv_id = apply_form.cleaned_data["invoice"]
        invoice = commerce.Invoice.objects.get(pk=inv_id)
        current_note.apply_to_invoice(invoice)
        messages.success(
            request,
            "Applied credit note %d to invoice." % note_id,
        )
        return redirect("invoice", invoice.id)

    elif request.POST and refund_form.is_valid():
        refund_form.instance.entered_by = request.user
        refund_form.instance.parent = current_note.credit_note
        refund_form.save()
        messages.success(
            request,
            "Applied manual refund to credit note."
        )
        refund_form = forms.ManualCreditNoteRefundForm(
            prefix="refund_note",
        )

    elif request.POST and cancellation_fee_form.is_valid():
        percentage = cancellation_fee_form.cleaned_data["percentage"]
        invoice = current_note.cancellation_fee(percentage)
        messages.success(
            request,
            "Generated cancellation fee for credit note %d." % note_id,
        )
        return redirect("invoice", invoice.invoice.id)

    data = {
        "credit_note": current_note.credit_note,
        "apply_form": apply_form,
        "refund_form": refund_form,
        "cancellation_fee_form": cancellation_fee_form,
    }

    return render(request, "registrasion/credit_note.html", data)


def amend_registration(request, user_id):
    ''' Allows staff to amend a user's current registration cart, and etc etc.
    '''

    user = User.objects.get(id=int(user_id))
    current_cart = CartController.for_user(user)

    items = commerce.ProductItem.objects.filter(
        cart=current_cart.cart,
    ).select_related("product")
    initial = [{"product": i.product, "quantity": i.quantity} for i in items]

    StaffProductsFormSet = forms.staff_products_formset_factory(user)
    formset = StaffProductsFormSet(
        request.POST or None,
        initial=initial,
        prefix="products",
    )

    for item, form in zip(items, formset):
        queryset = inventory.Product.objects.filter(id=item.product.id)
        form.fields["product"].queryset = queryset

    voucher_form = forms.VoucherForm(
        request.POST or None,
        prefix="voucher",
    )

    if request.POST and formset.is_valid():

        pq = [
            (f.cleaned_data["product"], f.cleaned_data["quantity"])
            for f in formset
            if "product" in f.cleaned_data and
            f.cleaned_data["product"] is not None
        ]

        try:
            current_cart.set_quantities(pq)
            return redirect(amend_registration, user_id)
        except ValidationError as ve:
            for ve_field in ve.error_list:
                product, message = ve_field.message
                for form in formset:
                    if "product" not in form.cleaned_data:
                        # This is the empty form.
                        continue
                    if form.cleaned_data["product"] == product:
                        form.add_error("quantity", message)

    if request.POST and voucher_form.has_changed() and voucher_form.is_valid():
        try:
            current_cart.apply_voucher(voucher_form.cleaned_data["voucher"])
            return redirect(amend_registration, user_id)
        except ValidationError as ve:
            voucher_form.add_error(None, ve)

    ic = ItemController(user)
    data = {
        "user": user,
        "paid": ic.items_purchased(),
        "cancelled": ic.items_released(),
        "form": formset,
        "voucher_form": voucher_form,
    }

    return render(request, "registrasion/amend_registration.html", data)

def amend_registration ( request , user_id ) : user = User . objects . get ( id = int ( user_id ) ) current_cart = CartController . for_user ( user ) items = commerce . ProductItem . objects . filter ( cart = current_cart . cart , ) . select_related ( "product" ) initial = [ { "product" : i . product , "quantity" : i . quantity } for i in items ] StaffProductsFormSet = forms . staff_products_formset_factory ( user ) formset = StaffProductsFormSet ( request . POST or None , initial = initial , prefix = "products" , ) for item , form in zip ( items , formset ) : queryset = inventory . Product . objects . filter ( id = item . product . id ) form . fields [ "product" ] . queryset = queryset voucher_form = forms . VoucherForm ( request . POST or None , prefix = "voucher" , ) if request . POST and formset . is_valid ( ) : pq = [ ( f . cleaned_data [ "product" ] , f . cleaned_data [ "quantity" ] ) for f in formset if "product" in f . cleaned_data and f . cleaned_data [ "product" ] is not None ] try : current_cart . set_quantities ( pq ) return redirect ( amend_registration , user_id ) except ValidationError as ve : for ve_field in ve . error_list : product , message = ve_field . message for form in formset : if "product" not in form . cleaned_data : continue if form . cleaned_data [ "product" ] == product : form . add_error ( "quantity" , message ) if request . POST and voucher_form . has_changed ( ) and voucher_form . is_valid ( ) : try : current_cart . apply_voucher ( voucher_form . cleaned_data [ "voucher" ] ) return redirect ( amend_registration , user_id ) except ValidationError as ve : voucher_form . add_error ( None , ve ) ic = ItemController ( user ) data = { "user" : user , "paid" : ic . items_purchased ( ) , "cancelled" : ic . items_released ( ) , "form" : formset , "voucher_form" : voucher_form , } return render ( request , "registrasion/amend_registration.html" , data )

Allows staff to amend a user s current registration cart and etc etc .


def extend_reservation(request, user_id, days=7):
    ''' Allows staff to extend the reservation on a given user's cart.
    '''

    user = User.objects.get(id=int(user_id))
    cart = CartController.for_user(user)
    cart.extend_reservation(datetime.timedelta(days=days))

    return redirect(request.META["HTTP_REFERER"])

def extend_reservation ( request , user_id , days = 7 ) : user = User . objects . get ( id = int ( user_id ) ) cart = CartController . for_user ( user ) cart . extend_reservation ( datetime . timedelta ( days = days ) ) return redirect ( request . META [ "HTTP_REFERER" ] )

Allows staff to extend the reservation on a given user s cart .


def invoice_mailout(request):
    ''' Allows staff to send emails to users based on their invoice status. '''

    category = request.GET.getlist("category", [])
    product = request.GET.getlist("product", [])
    status = request.GET.get("status")

    form = forms.InvoiceEmailForm(
        request.POST or None,
        category=category,
        product=product,
        status=status,
    )

    emails = []

    if form.is_valid():
        emails = []
        for invoice in form.cleaned_data["invoice"]:
            # datatuple = (subject, message, from_email, recipient_list)
            from_email = form.cleaned_data["from_email"]
            subject = form.cleaned_data["subject"]
            body = Template(form.cleaned_data["body"]).render(
                Context({
                    "invoice": invoice,
                    "user": invoice.user,
                })
            )
            recipient_list = [invoice.user.email]
            emails.append(Email(subject, body, from_email, recipient_list))

        if form.cleaned_data["action"] == forms.InvoiceEmailForm.ACTION_SEND:
            # Send e-mails *ONLY* if we're sending.
            send_mass_mail(emails)
            messages.info(request, "The e-mails have been sent.")

    data = {
        "form": form,
        "emails": emails,
    }

    return render(request, "registrasion/invoice_mailout.html", data)

def
invoice_mailout
(
request
)
:
category
=
request
.
GET
.
getlist
(
"category"
,
[
]
)
product
=
request
.
GET
.
getlist
(
"product"
,
[
]
)
status
=
request
.
GET
.
get
(
"status"
)
form
=
forms
.
InvoiceEmailForm
(
request
.
POST
or
,
category
=
category
,
product
=
product
,
status
=
status
,
)
emails
=
[
]
if
form
.
is_valid
(
)
:
emails
=
[
]
for
invoice
in
form
.
cleaned_data
[
"invoice"
]
:
from_email
=
form
.
cleaned_data
[
"from_email"
]
subject
=
form
.
cleaned_data
[
"subject"
]
body
=
Template
(
form
.
cleaned_data
[
"body"
]
)
.
render
(
Context
(
{
"invoice"
:
invoice
,
"user"
:
invoice
.
user
,
}
)
)
recipient_list
=
[
invoice
.
user
.
email
]
emails
.
append
(
Email
(
subject
,
body
,
from_email
,
recipient_list
)
)
if
form
.
cleaned_data
[
"action"
]
==
forms
.
InvoiceEmailForm
.
ACTION_SEND
:
send_mass_mail
(
emails
)
messages
.
info
(
request
,
"The e-mails have been sent."
)
data
=
{
"form"
:
form
,
"emails"
:
emails
,
}
return
render
(
request
,
"registrasion/invoice_mailout.html"
,
data
)

Allows staff to send emails to users based on their invoice status .


def badges(request):
    ''' Either displays a form containing a list of users with badges to
    render, or returns a .zip file containing their badges. '''

    category = request.GET.getlist("category", [])
    product = request.GET.getlist("product", [])
    status = request.GET.get("status")

    form = forms.InvoicesWithProductAndStatusForm(
        request.POST or None,
        category=category,
        product=product,
        status=status,
    )

    if form.is_valid():
        response = HttpResponse()
        response["Content-Type"] = "application.zip"
        response["Content-Disposition"] = 'attachment; filename="badges.zip"'

        z = zipfile.ZipFile(response, "w")

        for invoice in form.cleaned_data["invoice"]:
            user = invoice.user
            badge = render_badge(user)
            z.writestr("badge_%d.svg" % user.id, badge.encode("utf-8"))

        return response

    data = {
        "form": form,
    }

    return render(request, "registrasion/badges.html", data)

def
badges
(
request
)
:
category
=
request
.
GET
.
getlist
(
"category"
,
[
]
)
product
=
request
.
GET
.
getlist
(
"product"
,
[
]
)
status
=
request
.
GET
.
get
(
"status"
)
form
=
forms
.
InvoicesWithProductAndStatusForm
(
request
.
POST
or
,
category
=
category
,
product
=
product
,
status
=
status
,
)
if
form
.
is_valid
(
)
:
response
=
HttpResponse
(
)
response
[
"Content-Type"
]
=
"application.zip"
response
[
"Content-Disposition"
]
=
'attachment; filename="badges.zip"'
z
=
zipfile
.
ZipFile
(
response
,
"w"
)
for
invoice
in
form
.
cleaned_data
[
"invoice"
]
:
user
=
invoice
.
user
badge
=
render_badge
(
user
)
z
.
writestr
(
"badge_%d.svg"
%
user
.
id
,
badge
.
encode
(
"utf-8"
)
)
return
response
data
=
{
"form"
:
form
,
}
return
render
(
request
,
"registrasion/badges.html"
,
data
)

Either displays a form containing a list of users with badges to render or returns a . zip file containing their badges .


def render_badge(user):
    ''' Renders a single user's badge. '''

    data = {
        "user": user,
    }

    t = loader.get_template('registrasion/badge.svg')
    return t.render(data)

def render_badge ( user ) : data = { "user" : user , } t = loader . get_template ( 'registrasion/badge.svg' ) return t . render ( data )


def available_discounts(cls, user, categories, products):
        ''' Returns all discounts available to this user for the given
        categories and products. The discounts also list the available quantity
        for this user, not including products that are pending purchase. '''

        filtered_clauses = cls._filtered_clauses(user)

        # clauses that match provided categories
        categories = set(categories)
        # clauses that match provided products
        products = set(products)
        # clauses that match categories for provided products
        product_categories = set(product.category for product in products)
        # (Not relevant: clauses that match products in provided categories)
        all_categories = categories | product_categories

        filtered_clauses = (
            clause for clause in filtered_clauses
            if hasattr(clause, 'product') and clause.product in products or
            hasattr(clause, 'category') and clause.category in all_categories
        )

        discounts = []

        # Markers so that we don't need to evaluate given conditions
        # more than once
        accepted_discounts = set()
        failed_discounts = set()

        for clause in filtered_clauses:
            discount = clause.discount
            cond = ConditionController.for_condition(discount)

            past_use_count = clause.past_use_count
            if past_use_count >= clause.quantity:
                # This clause has exceeded its use count
                pass
            elif discount not in failed_discounts:
                # This clause is still available
                is_accepted = discount in accepted_discounts
                if is_accepted or cond.is_met(user, filtered=True):
                    # This clause is valid for this user
                    discounts.append(DiscountAndQuantity(
                        discount=discount,
                        clause=clause,
                        quantity=clause.quantity - past_use_count,
                    ))
                    accepted_discounts.add(discount)
                else:
                    # This clause is not valid for this user
                    failed_discounts.add(discount)
        return discounts

def available_discounts ( cls , user , categories , products ) : filtered_clauses = cls . _filtered_clauses ( user ) categories = set ( categories ) products = set ( products ) product_categories = set ( product . category for product in products ) all_categories = categories | product_categories filtered_clauses = ( clause for clause in filtered_clauses if hasattr ( clause , 'product' ) and clause . product in products or hasattr ( clause , 'category' ) and clause . category in all_categories ) discounts = [ ] accepted_discounts = set ( ) failed_discounts = set ( ) for clause in filtered_clauses : discount = clause . discount cond = ConditionController . for_condition ( discount ) past_use_count = clause . past_use_count if past_use_count >= clause . quantity : pass elif discount not in failed_discounts : is_accepted = discount in accepted_discounts if is_accepted or cond . is_met ( user , filtered = True ) : discounts . append ( DiscountAndQuantity ( discount = discount , clause = clause , quantity = clause . quantity - past_use_count , ) ) accepted_discounts . add ( discount ) else : failed_discounts . add ( discount ) return discounts

Returns all discounts available to this user for the given categories and products . The discounts also list the available quantity for this user not including products that are pending purchase .


def _annotate_with_past_uses(cls, queryset, user):
        ''' Annotates the queryset with a usage count for that discount claus
        by the given user. '''

        if queryset.model == conditions.DiscountForCategory:
            matches = (
                Q(category=F('discount__discountitem__product__category'))
            )
        elif queryset.model == conditions.DiscountForProduct:
            matches = (
                Q(product=F('discount__discountitem__product'))
            )

        in_carts = (
            Q(discount__discountitem__cart__user=user) &
            Q(discount__discountitem__cart__status=commerce.Cart.STATUS_PAID)
        )

        past_use_quantity = When(
            in_carts & matches,
            then="discount__discountitem__quantity",
        )

        past_use_quantity_or_zero = Case(
            past_use_quantity,
            default=Value(0),
        )

        queryset = queryset.annotate(
            past_use_count=Sum(past_use_quantity_or_zero)
        )
        return queryset

Annotates the queryset with a usage count for that discount claus by the given user .


def available_products(cls, user, category=None, products=None):
        ''' Returns a list of all of the products that are available per
        flag conditions from the given categories. '''
        if category is None and products is None:
            raise ValueError("You must provide products or a category")

        if category is not None:
            all_products = inventory.Product.objects.filter(category=category)
            all_products = all_products.select_related("category")
        else:
            all_products = []

        if products is not None:
            all_products = set(itertools.chain(all_products, products))

        category_remainders = CategoryController.user_remainders(user)
        product_remainders = ProductController.user_remainders(user)

        passed_limits = set(
            product
            for product in all_products
            if category_remainders[product.category.id] > 0
            if product_remainders[product.id] > 0
        )

        failed_and_messages = FlagController.test_flags(
            user, products=passed_limits
        )
        failed_conditions = set(i[0] for i in failed_and_messages)

        out = list(passed_limits - failed_conditions)
        out.sort(key=lambda product: product.order)

        return out

Returns a list of all of the products that are available per flag conditions from the given categories .


def apply_to_invoice(self, invoice):
        ''' Applies the total value of this credit note to the specified
        invoice. If this credit note overpays the invoice, a new credit note
        containing the residual value will be created.

        Raises ValidationError if the given invoice is not allowed to be
        paid.
        '''

        # Local import to fix import cycles. Can we do better?
        from .invoice import InvoiceController
        inv = InvoiceController(invoice)
        inv.validate_allowed_to_pay()

        # Apply payment to invoice
        commerce.CreditNoteApplication.objects.create(
            parent=self.credit_note,
            invoice=invoice,
            amount=self.credit_note.value,
            reference="Applied credit note #%d" % self.credit_note.id,
        )

        inv.update_status()

def
apply_to_invoice
(
self
,
invoice
)
:
from
.
invoice
import
InvoiceController
inv
=
InvoiceController
(
invoice
)
inv
.
validate_allowed_to_pay
(
)
commerce
.
CreditNoteApplication
.
objects
.
create
(
parent
=
self
.
credit_note
,
invoice
=
invoice
,
amount
=
self
.
credit_note
.
value
,
reference
=
"Applied credit note #%d"
%
self
.
credit_note
.
id
,
)
inv
.
update_status
(
)

Applies the total value of this credit note to the specified invoice . If this credit note overpays the invoice a new credit note containing the residual value will be created .


def cancellation_fee(self, percentage):
        ''' Generates an invoice with a cancellation fee, and applies
        credit to the invoice.

        percentage (Decimal): The percentage of the credit note to turn into
        a cancellation fee. Must be 0 <= percentage <= 100.
        '''

        # Local import to fix import cycles. Can we do better?
        from .invoice import InvoiceController

        assert(percentage >= 0 and percentage <= 100)

        cancellation_fee = self.credit_note.value * percentage / 100
        due = datetime.timedelta(days=1)
        item = [("Cancellation fee", cancellation_fee)]
        invoice = InvoiceController.manual_invoice(
            self.credit_note.invoice.user, due, item
        )

        if not invoice.is_paid:
            self.apply_to_invoice(invoice)

        return InvoiceController(invoice)

def
cancellation_fee
(
self
,
percentage
)
:
from
.
invoice
import
InvoiceController
assert
(
percentage
>=
0
and
percentage
<=
100
)
cancellation_fee
=
self
.
credit_note
.
value
*
percentage
/
100
due
=
datetime
.
timedelta
(
days
=
1
)
item
=
[
(
"Cancellation fee"
,
cancellation_fee
)
]
invoice
=
InvoiceController
.
manual_invoice
(
self
.
credit_note
.
invoice
.
user
,
due
,
item
)
if
not
invoice
.
is_paid
:
self
.
apply_to_invoice
(
invoice
)
return
InvoiceController
(
invoice
)

Generates an invoice with a cancellation fee and applies credit to the invoice .


def generate_access_code():
    ''' Generates an access code for users' payments as well as their
    fulfilment code for check-in.
    The access code will 4 characters long, which allows for 1,500,625
    unique codes, which really should be enough for anyone. '''

    length = 6
    # all upper-case letters + digits 1-9 (no 0 vs O confusion)
    chars = string.uppercase + string.digits[1:]
    # 6 chars => 35 ** 6 = 1838265625 (should be enough for anyone)
    return get_random_string(length=length, allowed_chars=chars)

def generate_access_code ( ) : length = 6 chars = string . uppercase + string . digits [ 1 : ] return get_random_string ( length = length , allowed_chars = chars )

Generates an access code for users payments as well as their fulfilment code for check - in . The access code will 4 characters long which allows for 1 500 625 unique codes which really should be enough for anyone .


def lazy(function, *args, **kwargs):
    ''' Produces a callable so that functions can be lazily evaluated in
    templates.

    Arguments:

        function (callable): The function to call at evaluation time.

        args: Positional arguments, passed directly to ``function``.

        kwargs: Keyword arguments, passed directly to ``function``.

    Return:

        callable: A callable that will evaluate a call to ``function`` with
            the specified arguments.

    '''

    NOT_EVALUATED = object()
    retval = [NOT_EVALUATED]

    def evaluate():
        if retval[0] is NOT_EVALUATED:
            retval[0] = function(*args, **kwargs)
        return retval[0]

    return evaluate

def lazy ( function , * args , ** kwargs ) : NOT_EVALUATED = object ( ) retval = [ NOT_EVALUATED ] def evaluate ( ) : if retval [ 0 ] is NOT_EVALUATED : retval [ 0 ] = function ( * args , ** kwargs ) return retval [ 0 ] return evaluate

Produces a callable so that functions can be lazily evaluated in templates .


def get_object_from_name(name):
    ''' Returns the named object.

    Arguments:
        name (str): A string of form `package.subpackage.etc.module.property`.
            This function will import `package.subpackage.etc.module` and
            return `property` from that module.

    '''

    dot = name.rindex(".")
    mod_name, property_name = name[:dot], name[dot + 1:]
    __import__(mod_name)
    return getattr(sys.modules[mod_name], property_name)

def get_object_from_name ( name ) : dot = name . rindex ( "." ) mod_name , property_name = name [ : dot ] , name [ dot + 1 : ] __import__ ( mod_name ) return getattr ( sys . modules [ mod_name ] , property_name )


def for_cart(cls, cart):
        ''' Returns an invoice object for a given cart at its current revision.
        If such an invoice does not exist, the cart is validated, and if valid,
        an invoice is generated.'''

        cart.refresh_from_db()
        try:
            invoice = commerce.Invoice.objects.exclude(
                status=commerce.Invoice.STATUS_VOID,
            ).get(
                cart=cart,
                cart_revision=cart.revision,
            )
        except ObjectDoesNotExist:
            cart_controller = CartController(cart)
            cart_controller.validate_cart()  # Raises ValidationError on fail.

            cls.update_old_invoices(cart)
            invoice = cls._generate_from_cart(cart)

        return cls(invoice)

def for_cart ( cls , cart ) : cart . refresh_from_db ( ) try : invoice = commerce . Invoice . objects . exclude ( status = commerce . Invoice . STATUS_VOID , ) . get ( cart = cart , cart_revision = cart . revision , ) except ObjectDoesNotExist : cart_controller = CartController ( cart ) cart_controller . validate_cart ( ) cls . update_old_invoices ( cart ) invoice = cls . _generate_from_cart ( cart ) return cls ( invoice )

Returns an invoice object for a given cart at its current revision . If such an invoice does not exist the cart is validated and if valid an invoice is generated .


def manual_invoice(cls, user, due_delta, description_price_pairs):
        ''' Generates an invoice for arbitrary items, not held in a user's
        cart.

        Arguments:
            user (User): The user the invoice is being generated for.
            due_delta (datetime.timedelta): The length until the invoice is
                due.
            description_price_pairs ([(str, long or Decimal), ...]): A list of
                pairs. Each pair consists of the description for each line item
                and the price for that line item. The price will be cast to
                Decimal.

        Returns:
            an Invoice.
        '''

        line_items = []
        for description, price in description_price_pairs:
            line_item = commerce.LineItem(
                description=description,
                quantity=1,
                price=Decimal(price),
                product=None,
            )
            line_items.append(line_item)

        min_due_time = timezone.now() + due_delta
        return cls._generate(user, None, min_due_time, line_items)

def manual_invoice ( cls , user , due_delta , description_price_pairs ) : line_items = [ ] for description , price in description_price_pairs : line_item = commerce . LineItem ( description = description , quantity = 1 , price = Decimal ( price ) , product = None , ) line_items . append ( line_item ) min_due_time = timezone . now ( ) + due_delta return cls . _generate ( user , None , min_due_time , line_items )

Generates an invoice for arbitrary items not held in a user s cart .


def _generate_from_cart(cls, cart):
        ''' Generates an invoice for the given cart. '''

        cart.refresh_from_db()

        # Generate the line items from the cart.

        product_items = commerce.ProductItem.objects.filter(cart=cart)
        product_items = product_items.select_related(
            "product",
            "product__category",
        )
        product_items = product_items.order_by(
            "product__category__order", "product__order"
        )

        if len(product_items) == 0:
            raise ValidationError("Your cart is empty.")

        discount_items = commerce.DiscountItem.objects.filter(cart=cart)
        discount_items = discount_items.select_related(
            "discount",
            "product",
            "product__category",
        )

        def format_product(product):
            return "%s - %s" % (product.category.name, product.name)

        def format_discount(discount, product):
            description = discount.description
            return "%s (%s)" % (description, format_product(product))

        line_items = []

        for item in product_items:
            product = item.product
            line_item = commerce.LineItem(
                description=format_product(product),
                quantity=item.quantity,
                price=product.price,
                product=product,
            )
            line_items.append(line_item)
        for item in discount_items:
            line_item = commerce.LineItem(
                description=format_discount(item.discount, item.product),
                quantity=item.quantity,
                price=cls.resolve_discount_value(item) * -1,
                product=item.product,
            )
            line_items.append(line_item)

        # Generate the invoice

        min_due_time = cart.reservation_duration + cart.time_last_updated

        return cls._generate(cart.user, cart, min_due_time, line_items)

def
_generate_from_cart
(
cls
,
cart
)
:
cart
.
refresh_from_db
(
)
product_items
=
commerce
.
ProductItem
.
objects
.
filter
(
cart
=
cart
)
product_items
=
product_items
.
select_related
(
"product"
,
"product__category"
,
)
product_items
=
product_items
.
order_by
(
"product__category__order"
,
"product__order"
)
if
len
(
product_items
)
==
0
:
raise
ValidationError
(
"Your cart is empty."
)
discount_items
=
commerce
.
DiscountItem
.
objects
.
filter
(
cart
=
cart
)
discount_items
=
discount_items
.
select_related
(
"discount"
,
"product"
,
"product__category"
,
)
def
format_product
(
product
)
:
return
"%s - %s"
%
(
product
.
category
.
name
,
product
.
name
)
def
format_discount
(
discount
,
product
)
:
description
=
discount
.
description
return
"%s (%s)"
%
(
description
,
format_product
(
product
)
)
line_items
=
[
]
for
item
in
product_items
:
product
=
item
.
product
line_item
=
commerce
.
LineItem
(
description
=
format_product
(
product
)
,
quantity
=
item
.
quantity
,
price
=
product
.
price
,
product
=
product
,
)
line_items
.
append
(
line_item
)
for
item
in
discount_items
:
line_item
=
commerce
.
LineItem
(
description
=
format_discount
(
item
.
discount
,
item
.
product
)
,
quantity
=
item
.
quantity
,
price
=
cls
.
resolve_discount_value
(
item
)
*
1
,
product
=
item
.
product
,
)
line_items
.
append
(
line_item
)
min_due_time
=
cart
.
reservation_duration
+
cart
.
time_last_updated
return
cls
.
_generate
(
cart
.
user
,
cart
,
min_due_time
,
line_items
)

Generates an invoice for the given cart .


def _apply_credit_notes(cls, invoice):
        ''' Applies the user's credit notes to the given invoice on creation.
        '''

        # We only automatically apply credit notes if this is the *only*
        # unpaid invoice for this user.
        invoices = commerce.Invoice.objects.filter(
            user=invoice.user,
            status=commerce.Invoice.STATUS_UNPAID,
        )
        if invoices.count() > 1:
            return

        notes = commerce.CreditNote.unclaimed().filter(
            invoice__user=invoice.user
        )
        for note in notes:
            try:
                CreditNoteController(note).apply_to_invoice(invoice)
            except ValidationError:
                # ValidationError will get raised once we're overpaying.
                break

        invoice.refresh_from_db()

def _apply_credit_notes ( cls , invoice ) : invoices = commerce . Invoice . objects . filter ( user = invoice . user , status = commerce . Invoice . STATUS_UNPAID , ) if invoices . count ( ) > 1 : return notes = commerce . CreditNote . unclaimed ( ) . filter ( invoice__user = invoice . user ) for note in notes : try : CreditNoteController ( note ) . apply_to_invoice ( invoice ) except ValidationError : break invoice . refresh_from_db ( )

Applies the user s credit notes to the given invoice on creation .


def can_view(self, user=None, access_code=None):
        ''' Returns true if the accessing user is allowed to view this invoice,
        or if the given access code matches this invoice's user's access code.
        '''

        if user == self.invoice.user:
            return True

        if user.is_staff:
            return True

        if self.invoice.user.attendee.access_code == access_code:
            return True

        return False

def can_view ( self , user = None , access_code = None ) : if user == self . invoice . user : return True if user . is_staff : return True if self . invoice . user . attendee . access_code == access_code : return True return False

Returns true if the accessing user is allowed to view this invoice or if the given access code matches this invoice s user s access code .


def _refresh(self):
        ''' Refreshes the underlying invoice and cart objects. '''
        self.invoice.refresh_from_db()
        if self.invoice.cart:
            self.invoice.cart.refresh_from_db()

def _refresh ( self ) : self . invoice . refresh_from_db ( ) if self . invoice . cart : self . invoice . cart . refresh_from_db ( )

Refreshes the underlying invoice and cart objects .


def validate_allowed_to_pay(self):
        ''' Passes cleanly if we're allowed to pay, otherwise raise
        a ValidationError. '''

        self._refresh()

        if not self.invoice.is_unpaid:
            raise ValidationError("You can only pay for unpaid invoices.")

        if not self.invoice.cart:
            return

        if not self._invoice_matches_cart():
            raise ValidationError("The registration has been amended since "
                                  "generating this invoice.")

        CartController(self.invoice.cart).validate_cart()

Passes cleanly if we re allowed to pay otherwise raise a ValidationError .


def update_status(self):
        ''' Updates the status of this invoice based upon the total
        payments.'''

        old_status = self.invoice.status
        total_paid = self.invoice.total_payments()
        num_payments = commerce.PaymentBase.objects.filter(
            invoice=self.invoice,
        ).count()
        remainder = self.invoice.value - total_paid

        if old_status == commerce.Invoice.STATUS_UNPAID:
            # Invoice had an amount owing
            if remainder <= 0:
                # Invoice no longer has amount owing
                self._mark_paid()

            elif total_paid == 0 and num_payments > 0:
                # Invoice has multiple payments totalling zero
                self._mark_void()
        elif old_status == commerce.Invoice.STATUS_PAID:
            if remainder > 0:
                # Invoice went from having a remainder of zero or less
                # to having a positive remainder -- must be a refund
                self._mark_refunded()
        elif old_status == commerce.Invoice.STATUS_REFUNDED:
            # Should not ever change from here
            pass
        elif old_status == commerce.Invoice.STATUS_VOID:
            # Should not ever change from here
            pass

        # Generate credit notes from residual payments
        residual = 0
        if self.invoice.is_paid:
            if remainder < 0:
                residual = 0 - remainder
        elif self.invoice.is_void or self.invoice.is_refunded:
            residual = total_paid

        if residual != 0:
            CreditNoteController.generate_from_invoice(self.invoice, residual)

        self.email_on_invoice_change(
            self.invoice,
            old_status,
            self.invoice.status,
        )

def update_status ( self ) : old_status = self . invoice . status total_paid = self . invoice . total_payments ( ) num_payments = commerce . PaymentBase . objects . filter ( invoice = self . invoice , ) . count ( ) remainder = self . invoice . value - total_paid if old_status == commerce . Invoice . STATUS_UNPAID : if remainder <= 0 : self . _mark_paid ( ) elif total_paid == 0 and num_payments > 0 : self . _mark_void ( ) elif old_status == commerce . Invoice . STATUS_PAID : if remainder > 0 : self . _mark_refunded ( ) elif old_status == commerce . Invoice . STATUS_REFUNDED : pass elif old_status == commerce . Invoice . STATUS_VOID : pass residual = 0 if self . invoice . is_paid : if remainder < 0 : residual = 0 - remainder elif self . invoice . is_void or self . invoice . is_refunded : residual = total_paid if residual != 0 : CreditNoteController . generate_from_invoice ( self . invoice , residual ) self . email_on_invoice_change ( self . invoice , old_status , self . invoice . status , )

Updates the status of this invoice based upon the total payments .


def _mark_paid(self):
        ''' Marks the invoice as paid, and updates the attached cart if
        necessary. '''
        cart = self.invoice.cart
        if cart:
            cart.status = commerce.Cart.STATUS_PAID
            cart.save()
        self.invoice.status = commerce.Invoice.STATUS_PAID
        self.invoice.save()

def _mark_paid ( self ) : cart = self . invoice . cart if cart : cart . status = commerce . Cart . STATUS_PAID cart . save ( ) self . invoice . status = commerce . Invoice . STATUS_PAID self . invoice . save ( )

Marks the invoice as paid and updates the attached cart if necessary .


def _invoice_matches_cart(self):
        ''' Returns true if there is no cart, or if the revision of this
        invoice matches the current revision of the cart. '''

        self._refresh()

        cart = self.invoice.cart
        if not cart:
            return True

        return cart.revision == self.invoice.cart_revision

def _invoice_matches_cart ( self ) : self . _refresh ( ) cart = self . invoice . cart if not cart : return True return cart . revision == self . invoice . cart_revision

Returns true if there is no cart or if the revision of this invoice matches the current revision of the cart .


def update_validity(self):
        ''' Voids this invoice if the attached cart is no longer valid because
        the cart revision has changed, or the reservations have expired. '''

        is_valid = self._invoice_matches_cart()
        cart = self.invoice.cart
        if self.invoice.is_unpaid and is_valid and cart:
            try:
                CartController(cart).validate_cart()
            except ValidationError:
                is_valid = False

        if not is_valid:
            if self.invoice.total_payments() > 0:
                # Free up the payments made to this invoice
                self.refund()
            else:
                self.void()

def update_validity ( self ) : is_valid = self . _invoice_matches_cart ( ) cart = self . invoice . cart if self . invoice . is_unpaid and is_valid and cart : try : CartController ( cart ) . validate_cart ( ) except ValidationError : is_valid = False if not is_valid : if self . invoice . total_payments ( ) > 0 : self . refund ( ) else : self . void ( )

Voids this invoice if the attached cart is no longer valid because the cart revision has changed or the reservations have expired .


def void(self):
        ''' Voids the invoice if it is valid to do so. '''
        if self.invoice.total_payments() > 0:
            raise ValidationError("Invoices with payments must be refunded.")
        elif self.invoice.is_refunded:
            raise ValidationError("Refunded invoices may not be voided.")
        if self.invoice.is_paid:
            self._release_cart()

        self._mark_void()

Voids the invoice if it is valid to do so .


def refund(self):
        ''' Refunds the invoice by generating a CreditNote for the value of
        all of the payments against the cart.

        The invoice is marked as refunded, and the underlying cart is marked
        as released.

        '''

        if self.invoice.is_void:
            raise ValidationError("Void invoices cannot be refunded")

        # Raises a credit note fot the value of the invoice.
        amount = self.invoice.total_payments()

        if amount == 0:
            self.void()
            return

        CreditNoteController.generate_from_invoice(self.invoice, amount)
        self.update_status()

def
refund
(
self
)
:
if
self
.
invoice
.
is_void
:
raise
ValidationError
(
"Void invoices cannot be refunded"
)
amount
=
self
.
invoice
.
total_payments
(
)
if
amount
==
0
:
self
.
void
(
)
return
CreditNoteController
.
generate_from_invoice
(
self
.
invoice
,
amount
)
self
.
update_status
(
)

Refunds the invoice by generating a CreditNote for the value of all of the payments against the cart .


def email(cls, invoice, kind):
        ''' Sends out an e-mail notifying the user about something to do
        with that invoice. '''

        context = {
            "invoice": invoice,
        }

        send_email([invoice.user.email], kind, context=context)

def email ( cls , invoice , kind ) : context = { "invoice" : invoice , } send_email ( [ invoice . user . email ] , kind , context = context )

Sends out an e - mail notifying the user about something to do with that invoice .


def update(self, data):
        """Update the object with new data."""
        fields = [
            'id',
            'status',
            'type',
            'persistence',
            'date_start',
            'date_finish',
            'date_created',
            'date_modified',
            'checksum',
            'processor_name',
            'input',
            'input_schema',
            'output',
            'output_schema',
            'static',
            'static_schema',
            'var',
            'var_template',
        ]

        self.annotation = {}
        for f in fields:
            setattr(self, f, data[f])

        self.name = data['static']['name'] if 'name' in data['static'] else ''

        self.annotation.update(self._flatten_field(data['input'], data['input_schema'], 'input'))
        self.annotation.update(self._flatten_field(data['output'], data['output_schema'], 'output'))
        self.annotation.update(self._flatten_field(data['static'], data['static_schema'], 'static'))
        self.annotation.update(self._flatten_field(data['var'], data['var_template'], 'var'))

def update ( self , data ) : fields = [ 'id' , 'status' , 'type' , 'persistence' , 'date_start' , 'date_finish' , 'date_created' , 'date_modified' , 'checksum' , 'processor_name' , 'input' , 'input_schema' , 'output' , 'output_schema' , 'static' , 'static_schema' , 'var' , 'var_template' , ] self . annotation = { } for f in fields : setattr ( self , f , data [ f ] ) self . name = data [ 'static' ] [ 'name' ] if 'name' in data [ 'static' ] else '' self . annotation . update ( self . _flatten_field ( data [ 'input' ] , data [ 'input_schema' ] , 'input' ) ) self . annotation . update ( self . _flatten_field ( data [ 'output' ] , data [ 'output_schema' ] , 'output' ) ) self . annotation . update ( self . _flatten_field ( data [ 'static' ] , data [ 'static_schema' ] , 'static' ) ) self . annotation . update ( self . _flatten_field ( data [ 'var' ] , data [ 'var_template' ] , 'var' ) )


def _flatten_field(self, field, schema, path):
        """Reduce dicts of dicts to dot separated keys."""
        flat = {}
        for field_schema, fields, path in iterate_schema(field, schema, path):
            name = field_schema['name']
            typ = field_schema['type']
            label = field_schema['label']
            value = fields[name] if name in fields else None
            flat[path] = {'name': name, 'value': value, 'type': typ, 'label': label}

        return flat

def _flatten_field ( self , field , schema , path ) : flat = { } for field_schema , fields , path in iterate_schema ( field , schema , path ) : name = field_schema [ 'name' ] typ = field_schema [ 'type' ] label = field_schema [ 'label' ] value = fields [ name ] if name in fields else None flat [ path ] = { 'name' : name , 'value' : value , 'type' : typ , 'label' : label } return flat

Reduce dicts of dicts to dot separated keys .


def print_downloads(self):
        """Print file fields to standard output."""
        for path, ann in self.annotation.items():
            if path.startswith('output') and ann['type'] == 'basic:file:':
                print("{}: {}".format(path, ann['value']['file']))

def
print_downloads
(
self
)
:
for
path
,
ann
in
self
.
annotation
.
items
(
)
:
if
path
.
startswith
(
'output'
)
and
ann
[
'type'
]
==
'basic:file:'
:
print
(
"{}: {}"
.
format
(
path
,
ann
[
'value'
]
[
'file'
]
)
)


def download(self, field):
        """Download a file.

        :param field: file field to download
        :type field: string
        :rtype: a file handle

        """
        if not field.startswith('output'):
            raise ValueError("Only processor results (output.* fields) can be downloaded")

        if field not in self.annotation:
            raise ValueError("Download field {} does not exist".format(field))

        ann = self.annotation[field]
        if ann['type'] != 'basic:file:':
            raise ValueError("Only basic:file: field can be downloaded")

        return next(self.gencloud.download([self.id], field))


def project_data(self, project):
        """Return a list of Data objects for given project.

        :param project: ObjectId or slug of Genesis project
        :type project: string
        :rtype: list of Data objects

        """
        projobjects = self.cache['project_objects']
        objects = self.cache['objects']
        project_id = str(project)

        if not re.match('^[0-9a-fA-F]{24}$', project_id):
            # project_id is a slug
            projects = self.api.case.get(url_slug=project_id)['objects']
            if len(projects) != 1:
                raise ValueError(msg='Attribute project not a slug or ObjectId: {}'.format(project_id))

            project_id = str(projects[0]['id'])

        if project_id not in projobjects:
            projobjects[project_id] = []
            data = self.api.data.get(case_ids__contains=project_id)['objects']
            for d in data:
                _id = d['id']
                if _id in objects:
                    # Update existing object
                    objects[_id].update(d)
                else:
                    # Insert new object
                    objects[_id] = GenData(d, self)

                projobjects[project_id].append(objects[_id])

            # Hydrate reference fields
            for d in projobjects[project_id]:
                while True:
                    ref_annotation = {}
                    remove_annotation = []
                    for path, ann in d.annotation.items():
                        if ann['type'].startswith('data:'):
                            # Referenced data object found
                            # Copy annotation
                            if ann['value'] in self.cache['objects']:
                                annotation = self.cache['objects'][ann['value']].annotation
                                ref_annotation.update({path + '.' + k: v for k, v in annotation.items()})

                            remove_annotation.append(path)
                    if ref_annotation:
                        d.annotation.update(ref_annotation)
                        for path in remove_annotation:
                            del d.annotation[path]
                    else:
                        break

        return projobjects[project_id]

Return a list of Data objects for given project .


def processors(self, processor_name=None):
        """Return a list of Processor objects.

        :param project_id: ObjectId of Genesis project
        :type project_id: string
        :rtype: list of Processor objects

        """
        if processor_name:
            return self.api.processor.get(name=processor_name)['objects']
        else:
            return self.api.processor.get()['objects']

def processors ( self , processor_name = None ) : if processor_name : return self . api . processor . get ( name = processor_name ) [ 'objects' ] else : return self . api . processor . get ( ) [ 'objects' ]


def print_processor_inputs(self, processor_name):
        """Print processor input fields and types.

        :param processor_name: Processor object name
        :type processor_name: string

        """
        p = self.processors(processor_name=processor_name)

        if len(p) == 1:
            p = p[0]
        else:
            Exception('Invalid processor name')

        for field_schema, _, _ in iterate_schema({}, p['input_schema'], 'input'):
            name = field_schema['name']
            typ = field_schema['type']
            # value = fields[name] if name in fields else None
            print("{} -> {}".format(name, typ))

def
print_processor_inputs
(
self
,
processor_name
)
:
p
=
self
.
processors
(
processor_name
=
processor_name
)
if
len
(
p
)
==
1
:
p
=
p
[
0
]
else
:
Exception
(
'Invalid processor name'
)
for
field_schema
,
_
,
_
in
iterate_schema
(
{
}
,
p
[
'input_schema'
]
,
'input'
)
:
name
=
field_schema
[
'name'
]
typ
=
field_schema
[
'type'
]
print
(
"{} -> {}"
.
format
(
name
,
typ
)
)

Print processor input fields and types .


def rundata(self, strjson):
        """POST JSON data object to server"""

        d = json.loads(strjson)
        return self.api.data.post(d)

def rundata ( self , strjson ) : d = json . loads ( strjson ) return self . api . data . post ( d )


def upload(self, project_id, processor_name, **fields):
        """Upload files and data objects.

        :param project_id: ObjectId of Genesis project
        :type project_id: string
        :param processor_name: Processor object name
        :type processor_name: string
        :param fields: Processor field-value pairs
        :type fields: args
        :rtype: HTTP Response object

        """
        p = self.processors(processor_name=processor_name)

        if len(p) == 1:
            p = p[0]
        else:
            Exception('Invalid processor name {}'.format(processor_name))

        for field_name, field_val in fields.items():
            if field_name not in p['input_schema']:
                Exception("Field {} not in processor {} inputs".format(field_name, p['name']))

            if find_field(p['input_schema'], field_name)['type'].startswith('basic:file:'):
                if not os.path.isfile(field_val):
                    Exception("File {} not found".format(field_val))

        inputs = {}

        for field_name, field_val in fields.items():
            if find_field(p['input_schema'], field_name)['type'].startswith('basic:file:'):

                file_temp = self._upload_file(field_val)

                if not file_temp:
                    Exception("Upload failed for {}".format(field_val))

                inputs[field_name] = {
                    'file': field_val,
                    'file_temp': file_temp
                }
            else:
                inputs[field_name] = field_val

        d = {
            'status': 'uploading',
            'case_ids': [project_id],
            'processor_name': processor_name,
            'input': inputs,
        }

        return self.create(d)

def
upload
(
self
,
project_id
,
processor_name
,
**
fields
)
:
p
=
self
.
processors
(
processor_name
=
processor_name
)
if
len
(
p
)
==
1
:
p
=
p
[
0
]
else
:
Exception
(
'Invalid processor name {}'
.
format
(
processor_name
)
)
for
field_name
,
field_val
in
fields
.
items
(
)
:
if
field_name
not
in
p
[
'input_schema'
]
:
Exception
(
"Field {} not in processor {} inputs"
.
format
(
field_name
,
p
[
'name'
]
)
)
if
find_field
(
p
[
'input_schema'
]
,
field_name
)
[
'type'
]
.
startswith
(
'basic:file:'
)
:
if
not
os
.
path
.
isfile
(
field_val
)
:
Exception
(
"File {} not found"
.
format
(
field_val
)
)
inputs
=
{
}
for
field_name
,
field_val
in
fields
.
items
(
)
:
if
find_field
(
p
[
'input_schema'
]
,
field_name
)
[
'type'
]
.
startswith
(
'basic:file:'
)
:
file_temp
=
self
.
_upload_file
(
field_val
)
if
not
file_temp
:
Exception
(
"Upload failed for {}"
.
format
(
field_val
)
)
inputs
[
field_name
]
=
{
'file'
:
field_val
,
'file_temp'
:
file_temp
}
else
:
inputs
[
field_name
]
=
field_val
d
=
{
'status'
:
'uploading'
,
'case_ids'
:
[
project_id
]
,
'processor_name'
:
processor_name
,
'input'
:
inputs
,
}
return
self
.
create
(
d
)


def _upload_file(self, fn):
        """Upload a single file on the platform.

        File is uploaded in chunks of 1,024 bytes.

        :param fn: File path
        :type fn: string

        """
        size = os.path.getsize(fn)
        counter = 0
        base_name = os.path.basename(fn)
        session_id = str(uuid.uuid4())

        with open(fn, 'rb') as f:
            while True:
                response = None
                chunk = f.read(CHUNK_SIZE)
                if not chunk:
                    break

                for i in range(5):
                    content_range = 'bytes {}-{}/{}'.format(counter * CHUNK_SIZE,
                                                            counter * CHUNK_SIZE + len(chunk) - 1, size)
                    if i > 0 and response is not None:
                        print("Chunk upload failed (error {}): repeating {}"
                              .format(response.status_code, content_range))

                    response = requests.post(urlparse.urljoin(self.url, 'upload/'),
                                             auth=self.auth,
                                             data=chunk,
                                             headers={
                                                 'Content-Disposition': 'attachment; filename="{}"'.format(base_name),
                                                 'Content-Length': size,
                                                 'Content-Range': content_range,
                                                 'Content-Type': 'application/octet-stream',
                                                 'Session-Id': session_id})

                    if response.status_code in [200, 201]:
                        break
                else:
                    # Upload of a chunk failed (5 retries)
                    return None

                progress = 100. * (counter * CHUNK_SIZE + len(chunk)) / size
                sys.stdout.write("\r{:.0f} % Uploading {}".format(progress, fn))
                sys.stdout.flush()
                counter += 1
        print()
        return session_id


def download(self, data_objects, field):
        """Download files of data objects.

        :param data_objects: Data object ids
        :type data_objects: list of UUID strings
        :param field: Download field name
        :type field: string
        :rtype: generator of requests.Response objects

        """
        if not field.startswith('output'):
            raise ValueError("Only processor results (output.* fields) can be downloaded")

        for o in data_objects:
            o = str(o)
            if re.match('^[0-9a-fA-F]{24}$', o) is None:
                raise ValueError("Invalid object id {}".format(o))

            if o not in self.cache['objects']:
                self.cache['objects'][o] = GenData(self.api.data(o).get(), self)

            if field not in self.cache['objects'][o].annotation:
                raise ValueError("Download field {} does not exist".format(field))

            ann = self.cache['objects'][o].annotation[field]
            if ann['type'] != 'basic:file:':
                raise ValueError("Only basic:file: field can be downloaded")

        for o in data_objects:
            ann = self.cache['objects'][o].annotation[field]
            url = urlparse.urljoin(self.url, 'data/{}/{}'.format(o, ann['value']['file']))
            yield requests.get(url, stream=True, auth=self.auth)


def get_subclasses(c):
    """Gets the subclasses of a class."""
    subclasses = c.__subclasses__()
    for d in list(subclasses):
        subclasses.extend(get_subclasses(d))
    return subclasses

def get_subclasses ( c ) : subclasses = c . __subclasses__ ( ) for d in list ( subclasses ) : subclasses . extend ( get_subclasses ( d ) ) return subclasses


def get_repo_and_project(self):
        """Returns repository and project."""
        app = self.app

        # Get repo
        repo = app.data.apply('github-repo', app.args.github_repo,
            app.prompt_repo,
            on_load=app.github.get_repo,
            on_save=lambda r: r.id
            )

        assert repo, "repository not found."

        # Get project
        project = app.data.apply('asana-project', app.args.asana_project,
            app.prompt_project,
            on_load=app.asana.projects.find_by_id,
            on_save=lambda p: p['id']
            )

        assert project, "project not found."

        # Set first issue
        first_issue = app.data.apply('first-issue', app.args.first_issue,
            "set the first issue to sync with [1 for new repos]",
            on_save=int)

        assert first_issue
        assert first_issue >= 0, "issue must be positive"

        app.sync_data()

        return repo, project


def get_variant_phenotypes_with_suggested_changes(variant_id_list):
    '''for each variant, yields evidence and associated phenotypes, both current and suggested'''
    variants = civic.get_variants_by_ids(variant_id_list)
    evidence = list()
    for variant in variants:
        evidence.extend(variant.evidence)
    for e in evidence:
        suggested_changes_url = f'https://civicdb.org/api/evidence_items/{e.id}/suggested_changes'
        resp = requests.get(suggested_changes_url)
        resp.raise_for_status()
        suggested_changes = dict()
        for suggested_change in resp.json():
            pheno_changes = suggested_change['suggested_changes'].get('phenotype_ids', None)
            if pheno_changes is None:
                continue
            a, b = pheno_changes
            added = set(b) - set(a)
            deleted = set(a) - set(b)
            rid = suggested_change['id']
            suggested_changes[rid] = {'added': added, 'deleted': deleted}
        yield e, {'suggested_changes': suggested_changes, 'current': set([x.id for x in e.phenotypes])}

for each variant yields evidence and associated phenotypes both current and suggested


def get_variant_phenotypes_with_suggested_changes_merged(variant_id_list):
    '''for each variant, yields evidence and merged phenotype from applying suggested changes to current'''
    for evidence, phenotype_status in get_variant_phenotypes_with_suggested_changes(variant_id_list):
        final = phenotype_status['current']
        for rid in sorted(phenotype_status['suggested_changes']):
            changes = phenotype_status['suggested_changes'][rid]
            final = final - changes['deleted']
            final = final | changes['added']
        if final:
            yield evidence, final

for each variant yields evidence and merged phenotype from applying suggested changes to current


def search_variants_by_coordinates(coordinate_query, search_mode='any'):
    """
    Search the cache for variants matching provided coordinates using the corresponding search mode.

    :param coordinate_query: A civic CoordinateQuery object
                        start: the genomic start coordinate of the query
                        stop: the genomic end coordinate of the query
                        chr: the GRCh37 chromosome of the query (e.g. "7", "X")
                        alt: the alternate allele at the coordinate [optional]

    :param search_mode: ['any', 'include_smaller', 'include_larger', 'exact']
                        any: any overlap between a query and a variant is a match
                        include_smaller: variants must fit within the coordinates of the query
                        include_larger: variants must encompass the coordinates of the query
                        exact: variants must match coordinates precisely, as well as alternate
                               allele, if provided
                        search_mode is 'exact' by default

    :return:            Returns a list of variant hashes matching the coordinates and search_mode
    """
    get_all_variants()
    ct = COORDINATE_TABLE
    start_idx = COORDINATE_TABLE_START
    stop_idx = COORDINATE_TABLE_STOP
    chr_idx = COORDINATE_TABLE_CHR
    start = int(coordinate_query.start)
    stop = int(coordinate_query.stop)
    chromosome = str(coordinate_query.chr)
    # overlapping = (start <= ct.stop) & (stop >= ct.start)
    left_idx = chr_idx.searchsorted(chromosome)
    right_idx = chr_idx.searchsorted(chromosome, side='right')
    chr_ct_idx = chr_idx[left_idx:right_idx].index
    right_idx = start_idx.searchsorted(stop, side='right')
    start_ct_idx = start_idx[:right_idx].index
    left_idx = stop_idx.searchsorted(start)
    stop_ct_idx = stop_idx[left_idx:].index
    match_idx = chr_ct_idx & start_ct_idx & stop_ct_idx
    m_df = ct.loc[match_idx, ]
    if search_mode == 'any':
        var_digests = m_df.v_hash.to_list()
        return [CACHE[v] for v in var_digests]
    elif search_mode == 'include_smaller':
        match_idx = (start <= m_df.start) & (stop >= m_df.stop)
    elif search_mode == 'include_larger':
        match_idx = (start >= m_df.start) & (stop <= m_df.stop)
    elif search_mode == 'exact':
        match_idx = (start == m_df.stop) & (stop == m_df.start)
        if coordinate_query.alt:
            match_idx = match_idx & (coordinate_query.alt == m_df.alt)
    else:
        raise ValueError("unexpected search mode")
    var_digests = m_df.loc[match_idx,].v_hash.to_list()
    return [CACHE[v] for v in var_digests]

def
search_variants_by_coordinates
(
coordinate_query
,
search_mode
=
'any'
)
:
get_all_variants
(
)
ct
=
COORDINATE_TABLE
start_idx
=
COORDINATE_TABLE_START
stop_idx
=
COORDINATE_TABLE_STOP
chr_idx
=
COORDINATE_TABLE_CHR
start
=
int
(
coordinate_query
.
start
)
stop
=
int
(
coordinate_query
.
stop
)
chromosome
=
str
(
coordinate_query
.
chr
)
left_idx
=
chr_idx
.
searchsorted
(
chromosome
)
right_idx
=
chr_idx
.
searchsorted
(
chromosome
,
side
=
'right'
)
chr_ct_idx
=
chr_idx
[
left_idx
:
right_idx
]
.
index
right_idx
=
start_idx
.
searchsorted
(
stop
,
side
=
'right'
)
start_ct_idx
=
start_idx
[
:
right_idx
]
.
index
left_idx
=
stop_idx
.
searchsorted
(
start
)
stop_ct_idx
=
stop_idx
[
left_idx
:
]
.
index
match_idx
=
chr_ct_idx
&
start_ct_idx
&
stop_ct_idx
m_df
=
ct
.
loc
[
match_idx
,
]
if
search_mode
==
'any'
:
var_digests
=
m_df
.
v_hash
.
to_list
(
)
return
[
CACHE
[
v
]
for
v
in
var_digests
]
elif
search_mode
==
'include_smaller'
:
match_idx
=
(
start
<=
m_df
.
start
)
&
(
stop
>=
m_df
.
stop
)
elif
search_mode
==
'include_larger'
:
match_idx
=
(
start
>=
m_df
.
start
)
&
(
stop
<=
m_df
.
stop
)
elif
search_mode
==
'exact'
:
match_idx
=
(
start
==
m_df
.
stop
)
&
(
stop
==
m_df
.
start
)
if
coordinate_query
.
alt
:
match_idx
=
match_idx
&
(
coordinate_query
.
alt
==
m_df
.
alt
)
else
:
raise
ValueError
(
"unexpected search mode"
)
var_digests
=
m_df
.
loc
[
match_idx
,
]
.
v_hash
.
to_list
(
)
return
[
CACHE
[
v
]
for
v
in
var_digests
]

Search the cache for variants matching provided coordinates using the corresponding search mode .


def bulk_search_variants_by_coordinates(sorted_queries, search_mode='any'):
    """
    An interator to search the cache for variants matching the set of sorted coordinates and yield
    matches corresponding to the search mode.

    :param sorted_queries:  A list of civic CoordinateQuery objects, sorted by coordinate.
                            start: the genomic start coordinate of the query
                            stop: the genomic end coordinate of the query
                            chr: the GRCh37 chromosome of the query (e.g. "7", "X")
                            alt: the alternate allele at the coordinate [optional]

    :param search_mode: ['any', 'include_smaller', 'include_larger', 'exact']
                        any: any overlap between a query and a variant is a match
                        include_smaller: variants must fit within the coordinates of the query
                        include_larger: variants must encompass the coordinates of the query
                        exact: variants must match coordinates precisely, as well as alternate
                               allele, if provided
                        search_mode is 'exact' by default

    :yield:            Yields (query, match) tuples for each identified match
    """

    def is_sorted(prev_q, current_q):
        if prev_q['chr'] < current_q['chr']:
            return True
        if prev_q['chr'] > current_q['chr']:
            return False
        if prev_q['start'] < current_q['start']:
            return True
        if prev_q['start'] > current_q['start']:
            return False
        if prev_q['stop'] < current_q['stop']:
            return True
        if prev_q['stop'] > current_q['stop']:
            return False
        return True

    ct_pointer = 0
    query_pointer = 0
    last_query_pointer = -1
    match_start = None
    ct = MODULE.COORDINATE_TABLE
    matches = defaultdict(list)
    Match = namedtuple('Match', ct.columns)
    while query_pointer < len(sorted_queries) and ct_pointer < len(ct):
        if last_query_pointer != query_pointer:
            q = sorted_queries[query_pointer]
            if match_start is not None:
                ct_pointer = match_start
                match_start = None
            last_query_pointer = query_pointer
        c = ct.iloc[ct_pointer]
        q_chr = str(q.chr)
        c_chr = c.chr
        if q_chr < c_chr:
            query_pointer += 1
            continue
        if q_chr > c_chr:
            ct_pointer += 1
            continue
        q_start = int(q.start)
        c_start = c.start
        q_stop = int(q.stop)
        c_stop = c.stop
        if q_start > c_stop:
            ct_pointer += 1
            continue
        if q_stop < c_start:
            query_pointer += 1
            continue
        if search_mode == 'any':
            matches[q].append(c.to_dict())
        elif search_mode == 'exact' and q_start == c_start and q_stop == c_stop:
            q_alt = q.alt
            c_alt = c.alt
            if not (q_alt and c_alt and q_alt != c_alt):
                matches[q].append(Match(**c.to_dict()))
        elif search_mode == 'include_smaller':
            raise NotImplementedError
        elif search_mode == 'include_larger':
            raise NotImplementedError
        if match_start is None:
            match_start = ct_pointer
        ct_pointer += 1
    return dict(matches)

def bulk_search_variants_by_coordinates ( sorted_queries , search_mode = 'any' ) : def is_sorted ( prev_q , current_q ) : if prev_q [ 'chr' ] < current_q [ 'chr' ] : return True if prev_q [ 'chr' ] > current_q [ 'chr' ] : return False if prev_q [ 'start' ] < current_q [ 'start' ] : return True if prev_q [ 'start' ] > current_q [ 'start' ] : return False if prev_q [ 'stop' ] < current_q [ 'stop' ] : return True if prev_q [ 'stop' ] > current_q [ 'stop' ] : return False return True ct_pointer = 0 query_pointer = 0 last_query_pointer = - 1 match_start = None ct = MODULE . COORDINATE_TABLE matches = defaultdict ( list ) Match = namedtuple ( 'Match' , ct . columns ) while query_pointer < len ( sorted_queries ) and ct_pointer < len ( ct ) : if last_query_pointer != query_pointer : q = sorted_queries [ query_pointer ] if match_start is not None : ct_pointer = match_start match_start = None last_query_pointer = query_pointer c = ct . iloc [ ct_pointer ] q_chr = str ( q . chr ) c_chr = c . chr if q_chr < c_chr : query_pointer += 1 continue if q_chr > c_chr : ct_pointer += 1 continue q_start = int ( q . start ) c_start = c . start q_stop = int ( q . stop ) c_stop = c . stop if q_start > c_stop : ct_pointer += 1 continue if q_stop < c_start : query_pointer += 1 continue if search_mode == 'any' : matches [ q ] . append ( c . to_dict ( ) ) elif search_mode == 'exact' and q_start == c_start and q_stop == c_stop : q_alt = q . alt c_alt = c . alt if not ( q_alt and c_alt and q_alt != c_alt ) : matches [ q ] . append ( Match ( ** c . to_dict ( ) ) ) elif search_mode == 'include_smaller' : raise NotImplementedError elif search_mode == 'include_larger' : raise NotImplementedError if match_start is None : match_start = ct_pointer ct_pointer += 1 return dict ( matches )

An interator to search the cache for variants matching the set of sorted coordinates and yield matches corresponding to the search mode .


def update(self, allow_partial=True, force=False, **kwargs):
        """Updates record and returns True if record is complete after update, else False."""
        if kwargs:
            self.__init__(partial=allow_partial, force=force, **kwargs)
            return not self._partial

        if not force and CACHE.get(hash(self)):
            cached = CACHE[hash(self)]
            for field in self._SIMPLE_FIELDS | self._COMPLEX_FIELDS:
                v = getattr(cached, field)
                setattr(self, field, v)
            self._partial = False
            logging.info(f'Loading {str(self)} from cache')
            return True
        resp_dict = element_lookup_by_id(self.type, self.id)
        self.__init__(partial=False, **resp_dict)
        return True

def
update
(
self
,
allow_partial
=
True
,
force
=
False
,
**
kwargs
)
:
if
kwargs
:
self
.
__init__
(
partial
=
allow_partial
,
force
=
force
,
**
kwargs
)
return
not
self
.
_partial
if
not
force
and
CACHE
.
get
(
hash
(
self
)
)
:
cached
=
CACHE
[
hash
(
self
)
]
for
field
in
self
.
_SIMPLE_FIELDS
|
self
.
_COMPLEX_FIELDS
:
v
=
getattr
(
cached
,
field
)
setattr
(
self
,
field
,
v
)
self
.
_partial
=
False
logging
.
info
(
f'Loading {str(self)} from cache'
)
return
True
resp_dict
=
element_lookup_by_id
(
self
.
type
,
self
.
id
)
self
.
__init__
(
partial
=
False
,
**
resp_dict
)
return
True

Updates record and returns True if record is complete after update else False .


def uniqify(cls, seq):
        """Returns a unique list of seq"""
        seen = set()
        seen_add = seen.add
        return [ x for x in seq if x not in seen and not seen_add(x)]

def uniqify ( cls , seq ) : seen = set ( ) seen_add = seen . add return [ x for x in seq if x not in seen and not seen_add ( x ) ]


def authenticate(self):
        """Connects to Github and Asana and authenticates via OAuth."""
        if self.oauth:
            return False

        # Save asana.
        self.settings.apply('api-asana', self.args.asana_api,
            "enter asana api key")

        # Save github.com
        self.settings.apply('api-github', self.args.github_api,
            "enter github.com token")

        logging.debug("authenticating asana api.")
        self.asana = Client.basic_auth(self.settings['api-asana'])
        self.asana_errors = asana_errors
        self.asana_me = self.asana.users.me()
        logging.debug("authenticating github api")
        self.github = Github(self.settings['api-github'])
        self.github_user = self.github.get_user()

        self.oauth = True

def
authenticate
(
self
)
:
if
self
.
oauth
:
return
False
self
.
settings
.
apply
(
'api-asana'
,
self
.
args
.
asana_api
,
"enter asana api key"
)
self
.
settings
.
apply
(
'api-github'
,
self
.
args
.
github_api
,
"enter github.com token"
)
logging
.
debug
(
"authenticating asana api."
)
self
.
asana
=
Client
.
basic_auth
(
self
.
settings
[
'api-asana'
]
)
self
.
asana_errors
=
asana_errors
self
.
asana_me
=
self
.
asana
.
users
.
me
(
)
logging
.
debug
(
"authenticating github api"
)
self
.
github
=
Github
(
self
.
settings
[
'api-github'
]
)
self
.
github_user
=
self
.
github
.
get_user
(
)
self
.
oauth
=
True

Connects to Github and Asana and authenticates via OAuth .


def _list_select(cls, lst, prompt, offset=0):
        """Given a list of values and names, accepts the index value or name."""

        inp = raw_input("select %s: " % prompt)
        assert inp, "value required."

        try:
            return lst[int(inp)+offset]
        except ValueError:
            return inp
        except IndexError:
            assert False, "bad value."

def
_list_select
(
cls
,
lst
,
prompt
,
offset
=
0
)
:
inp
=
raw_input
(
"select %s: "
%
prompt
)
assert
inp
,
"value required."
try
:
return
lst
[
int
(
inp
)
+
offset
]
except
ValueError
:
return
inp
except
IndexError
:
assert
False
,
"bad value."

Given a list of values and names accepts the index value or name .


def get_saved_issue_data(self, issue, namespace='open'):
        """Returns issue data from local data.

        Args:
            issue:
                `int`. Github issue number.
            namespace:
                `str`. Namespace for storing this issue.
        """

        if isinstance(issue, int):
            issue_number = str(issue)
        elif isinstance(issue, basestring):
            issue_number = issue
        else:
            issue_number = issue.number

        issue_data_key = self._issue_data_key(namespace)
        issue_data = self.data.get(issue_data_key,
            {})

        _data = issue_data.get(str(issue_number), {})
        issue_data[str(issue_number)] = _data
        return _data

def get_saved_issue_data ( self , issue , namespace = 'open' ) : if isinstance ( issue , int ) : issue_number = str ( issue ) elif isinstance ( issue , basestring ) : issue_number = issue else : issue_number = issue . number issue_data_key = self . _issue_data_key ( namespace ) issue_data = self . data . get ( issue_data_key , { } ) _data = issue_data . get ( str ( issue_number ) , { } ) issue_data [ str ( issue_number ) ] = _data return _data


def move_saved_issue_data(self, issue, ns, other_ns):
        """Moves an issue_data from one namespace to another."""

        if isinstance(issue, int):
            issue_number = str(issue)
        elif isinstance(issue, basestring):
            issue_number = issue
        else:
            issue_number = issue.number

        issue_data_key = self._issue_data_key(ns)
        other_issue_data_key = self._issue_data_key(other_ns)
        issue_data = self.data.get(issue_data_key,
            {})
        other_issue_data = self.data.get(other_issue_data_key,
            {})

        _id = issue_data.pop(issue_number, None)
        if _id:
            other_issue_data[issue_number] = _id

        self.data[other_issue_data_key] = other_issue_data
        self.data[issue_data_key] = issue_data

def move_saved_issue_data ( self , issue , ns , other_ns ) : if isinstance ( issue , int ) : issue_number = str ( issue ) elif isinstance ( issue , basestring ) : issue_number = issue else : issue_number = issue . number issue_data_key = self . _issue_data_key ( ns ) other_issue_data_key = self . _issue_data_key ( other_ns ) issue_data = self . data . get ( issue_data_key , { } ) other_issue_data = self . data . get ( other_issue_data_key , { } ) _id = issue_data . pop ( issue_number , None ) if _id : other_issue_data [ issue_number ] = _id self . data [ other_issue_data_key ] = other_issue_data self . data [ issue_data_key ] = issue_data

Moves an issue_data from one namespace to another .


def get_saved_task_data(self, task):
        """Returns task data from local data.

        Args:
            task:
                `int`. Asana task number.
        """

        if isinstance(task, int):
            task_number = str(task)
        elif isinstance(task, basestring):
            task_number = task
        else:
            task_number = task['id']

        task_data_key = self._task_data_key()
        task_data = self.data.get(task_data_key, {})

        _data = task_data.get(str(task_number), {})
        task_data[str(task_number)] = _data
        return _data

def get_saved_task_data ( self , task ) : if isinstance ( task , int ) : task_number = str ( task ) elif isinstance ( task , basestring ) : task_number = task else : task_number = task [ 'id' ] task_data_key = self . _task_data_key ( ) task_data = self . data . get ( task_data_key , { } ) _data = task_data . get ( str ( task_number ) , { } ) task_data [ str ( task_number ) ] = _data return _data


def get_asana_task(self, asana_task_id):
        """Retrieves a task from asana."""

        try:
            return self.asana.tasks.find_by_id(asana_task_id)
        except asana_errors.NotFoundError:
            return None
        except asana_errors.ForbiddenError:
            return None

def get_asana_task ( self , asana_task_id ) : try : return self . asana . tasks . find_by_id ( asana_task_id ) except asana_errors . NotFoundError : return None except asana_errors . ForbiddenError : return None


def save(self):
        """Save data."""

        with open(self.filename, 'wb') as file:
            self.prune()
            self.data['version'] = self.version
            json.dump(self.data,
                file,
                sort_keys=True, indent=2)

def save ( self ) : with open ( self . filename , 'wb' ) as file : self . prune ( ) self . data [ 'version' ] = self . version json . dump ( self . data , file , sort_keys = True , indent = 2 )


def apply(self, key, value, prompt=None,
        on_load=lambda a: a, on_save=lambda a: a):
        """Applies a setting value to a key, if the value is not `None`.

        Returns without prompting if either of the following:
            * `value` is not `None`
            * already present in the dictionary

        Args:
            prompt:
                May either be a string to prompt via `raw_input` or a
                method (callable) that returns the value.

            on_load:
                lambda. Value is passed through here after loaded.

            on_save:
                lambda. Value is saved as this value.
        """

        # Reset value if flag exists without value
        if value == '':
            value = None
            if key and self.data.has_key(key): del self.data[key]

        # If value is explicitly set from args.
        if value is not None:
            value = on_load(value)
            if key: self.data[key] = on_save(value)
            return value

        elif not key or not self.has_key(key):
            if callable(prompt):
                value = prompt()
            elif prompt is not None:
                value = raw_input(prompt + ": ")

            if value is None:
                if self.data.has_key(key): del self.data[key]
                return None

            self.data[key] = on_save(value)
            return value

        return on_load(self.data[key])

def
apply
(
self
,
key
,
value
,
prompt
=
,
on_load
=
lambda
a
:
a
,
on_save
=
lambda
a
:
a
)
:
if
value
==
''
:
value
=
if
key
and
self
.
data
.
has_key
(
key
)
:
del
self
.
data
[
key
]
if
value
is
not
:
value
=
on_load
(
value
)
if
key
:
self
.
data
[
key
]
=
on_save
(
value
)
return
value
elif
not
key
or
not
self
.
has_key
(
key
)
:
if
callable
(
prompt
)
:
value
=
prompt
(
)
elif
prompt
is
not
:
value
=
raw_input
(
prompt
+
": "
)
if
value
is
:
if
self
.
data
.
has_key
(
key
)
:
del
self
.
data
[
key
]
return
self
.
data
[
key
]
=
on_save
(
value
)
return
value
return
on_load
(
self
.
data
[
key
]
)

Applies a setting value to a key if the value is not None .


def transport_task(func):
    """Decorator for retrying tasks with special cases."""

    def wrapped_func(*args, **kwargs):
        tries = 0
        while True:
            try:
                try:
                    return func(*args, **kwargs)

                except (asana_errors.InvalidRequestError,
                        asana_errors.NotFoundError), exc:
                    logging.warn("warning: invalid request: %r", exc)

                except asana_errors.ForbiddenError, exc:
                    logging.warn("forbidden error: %r", exc)

                except asana_errors.NotFoundError, exc:
                    logging.warn("not found error: %r", exc)

                return None
            except asana_errors.RetryableAsanaError, retry_exc:
                tries += 1
                logging.warn("retry exception %r on try %d", retry_exc, tries)

                if tries >= 3:
                    raise
            except Exception, exc:
                logging.exception("Exception in transport.")
                return

    return wrapped_func

def
transport_task
(
func
)
:
def
wrapped_func
(
*
args
,
**
kwargs
)
:
tries
=
0
while
True
:
try
:
try
:
return
func
(
*
args
,
**
kwargs
)
except
(
asana_errors
.
InvalidRequestError
,
asana_errors
.
NotFoundError
)
,
exc
:
logging
.
warn
(
"warning: invalid request: %r"
,
exc
)
except
asana_errors
.
ForbiddenError
,
exc
:
logging
.
warn
(
"forbidden error: %r"
,
exc
)
except
asana_errors
.
NotFoundError
,
exc
:
logging
.
warn
(
"not found error: %r"
,
exc
)
return
except
asana_errors
.
RetryableAsanaError
,
retry_exc
:
tries
+=
1
logging
.
warn
(
"retry exception %r on try %d"
,
retry_exc
,
tries
)
if
tries
>=
3
:
raise
except
Exception
,
exc
:
logging
.
exception
(
"Exception in transport."
)
return
return
wrapped_func

Decorator for retrying tasks with special cases .