def patch(self, endpoint, data, headers=None, inception=False):
        """
        Method to update an item

        The headers must include an If-Match containing the object _etag.
            headers = {'If-Match': contact_etag}

        The data dictionary contain the fields that must be modified.

        If the patching fails because the _etag object do not match with the provided one, a
        BackendException is raised with code = 412.

        If inception is True, this method makes e new get request on the endpoint to refresh the
        _etag and then a new patch is called.

        If an HTTP 412 error occurs, a BackendException is raised. This exception is:
        - code: 412
        - message: response content
        - response: backend response

        All other HTTP error raises a BackendException.
        If some _issues are provided by the backend, this exception is:
        - code: HTTP error code
        - message: response content
        - response: JSON encoded backend response (including '_issues' dictionary ...)

        If no _issues are provided and an _error is signaled by the backend, this exception is:
        - code: backend error code
        - message: backend error message
        - response: JSON encoded backend response

        :param endpoint: endpoint (API URL)
        :type endpoint: str
        :param data: properties of item to update
        :type data: dict
        :param headers: headers (example: Content-Type). 'If-Match' required
        :type headers: dict
        :param inception: if True tries to get the last _etag
        :type inception: bool
        :return: dictionary containing patch response from the backend
        :rtype: dict
        """
        if not headers:
            raise BackendException(BACKEND_ERROR, "Header If-Match required for patching an object")

        response = self.get_response(method='PATCH', endpoint=endpoint, json=data, headers=headers)

        if response.status_code == 200:
            return self.decode(response=response)

        if response.status_code == 412:
            # 412 means Precondition failed, but confirm ...
            if inception:
                # update etag and retry to patch
                resp = self.get(endpoint)
                headers = {'If-Match': resp['_etag']}
                return self.patch(endpoint, data=data, headers=headers, inception=False)

            raise BackendException(response.status_code, response.content)
        else:  # pragma: no cover - should never occur
            raise BackendException(response.status_code, response.content)


def delete(self, endpoint, headers):
        """
        Method to delete an item or all items

        headers['If-Match'] must contain the _etag identifier of the element to delete

        :param endpoint: endpoint (API URL)
        :type endpoint: str
        :param headers: headers (example: Content-Type)
        :type headers: dict
        :return: response (deletion information)
        :rtype: dict
        """
        response = self.get_response(method='DELETE', endpoint=endpoint, headers=headers)

        logger.debug("delete, response: %s", response)
        if response.status_code != 204:  # pragma: no cover - should not happen ...
            resp = self.decode(response=response)

        resp = {"_status": "OK"}
        return resp


def samefile(path1, path2):
    """
    Returns True if path1 and path2 refer to the same file.
    """
    # Check if both are on the same volume and have the same file ID
    info1 = fs.getfileinfo(path1)
    info2 = fs.getfileinfo(path2)
    return (info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber and
            info1.nFileIndexHigh == info2.nFileIndexHigh and
            info1.nFileIndexLow == info2.nFileIndexLow)

Returns True if path1 and path2 refer to the same file .


def create(source, link_name):
    """
    Create a junction at link_name pointing to source.
    """
    success = False
    if not os.path.isdir(source):
        raise Exception("%s is not a directory" % source)
    if os.path.exists(link_name):
        raise Exception("%s: junction link name already exists" % link_name)

    link_name = os.path.abspath(link_name)
    os.mkdir(link_name)

    # Get a handle to the directory
    hlink = CreateFile(link_name, fs.GENERIC_WRITE,
        fs.FILE_SHARE_READ | fs.FILE_SHARE_WRITE, None, fs.OPEN_EXISTING,
        fs.FILE_FLAG_OPEN_REPARSE_POINT | fs.FILE_FLAG_BACKUP_SEMANTICS,
        None)
    try:
        if hlink == fs.INVALID_HANDLE_VALUE:
            raise WinError()

        srcvolpath = unparsed_convert(source)
        (junctioninfo, infolen) = new_junction_reparse_buffer(srcvolpath)

        dummy = DWORD(0)
        res = DeviceIoControl(
            hlink,
            FSCTL_SET_REPARSE_POINT,
            byref(junctioninfo),
            infolen,
            None,
            0,
            byref(dummy),
            None)

        if res == 0:
            raise WinError()
        success = True
    finally:
        if hlink != fs.INVALID_HANDLE_VALUE:
            CloseHandle(hlink)
        if not success:
            os.rmdir(link_name)

def
create
(
source
,
link_name
)
:
success
=
False
if
not
os
.
path
.
isdir
(
source
)
:
raise
Exception
(
"%s is not a directory"
%
source
)
if
os
.
path
.
exists
(
link_name
)
:
raise
Exception
(
"%s: junction link name already exists"
%
link_name
)
link_name
=
os
.
path
.
abspath
(
link_name
)
os
.
mkdir
(
link_name
)
# Get a handle to the directory
hlink
=
CreateFile
(
link_name
,
fs
.
GENERIC_WRITE
,
fs
.
FILE_SHARE_READ
|
fs
.
FILE_SHARE_WRITE
,
,
fs
.
OPEN_EXISTING
,
fs
.
FILE_FLAG_OPEN_REPARSE_POINT
|
fs
.
FILE_FLAG_BACKUP_SEMANTICS
,
)
try
:
if
hlink
==
fs
.
INVALID_HANDLE_VALUE
:
raise
WinError
(
)
srcvolpath
=
unparsed_convert
(
source
)
(
junctioninfo
,
infolen
)
=
new_junction_reparse_buffer
(
srcvolpath
)
dummy
=
DWORD
(
0
)
res
=
DeviceIoControl
(
hlink
,
FSCTL_SET_REPARSE_POINT
,
byref
(
junctioninfo
)
,
infolen
,
,
0
,
byref
(
dummy
)
,
)
if
res
==
0
:
raise
WinError
(
)
success
=
True
finally
:
if
hlink
!=
fs
.
INVALID_HANDLE_VALUE
:
CloseHandle
(
hlink
)
if
not
success
:
os
.
rmdir
(
link_name
)

Create a junction at link_name pointing to source .


def initialize_logger(args):
    """Sets command name and formatting for subsequent calls to logger"""

    global log_filename
    log_filename = os.path.join(os.getcwd(), "jacquard.log")
    if args.log_file:
        _validate_log_file(args.log_file)
        log_filename = args.log_file

    logging.basicConfig(format=_FILE_LOG_FORMAT,
                        level="DEBUG",
                        datefmt=_DATE_FORMAT,
                        filename=log_filename)

    global _verbose
    if args.verbose:
        _verbose = args.verbose

    start_time = datetime.now().strftime(_DATE_FORMAT)
    global _logging_dict
    _logging_dict = {'user': getpass.getuser(),
                     'host': socket.gethostname(),
                     'start_time': start_time,
                     'tool': args.subparser_name}

def initialize_logger ( args ) : global log_filename log_filename = os . path . join ( os . getcwd ( ) , "jacquard.log" ) if args . log_file : _validate_log_file ( args . log_file ) log_filename = args . log_file logging . basicConfig ( format = _FILE_LOG_FORMAT , level = "DEBUG" , datefmt = _DATE_FORMAT , filename = log_filename ) global _verbose if args . verbose : _verbose = args . verbose start_time = datetime . now ( ) . strftime ( _DATE_FORMAT ) global _logging_dict _logging_dict = { 'user' : getpass . getuser ( ) , 'host' : socket . gethostname ( ) , 'start_time' : start_time , 'tool' : args . subparser_name }


def error(self, message):
        '''Suppress default exit behavior'''
        message = self._remessage_invalid_subparser(message)
        raise utils.UsageError(message)

def error ( self , message ) : message = self . _remessage_invalid_subparser ( message ) raise utils . UsageError ( message )


def claim(self, file_readers):
        """Recognizes and claims MuTect VCFs form the set of all input VCFs.

        Each defined caller has a chance to evaluate and claim all the incoming
        files as something that it can process.

        Args:
            file_readers: the collection of currently unclaimed files

        Returns:
            A tuple of unclaimed readers and MuTectVcfReaders.
        """
        unclaimed_readers = []
        vcf_readers = []
        for file_reader in file_readers:
            if self._is_mutect_vcf(file_reader):
                vcf_reader = vcf.VcfReader(file_reader)
                vcf_readers.append(_MutectVcfReader(vcf_reader))
            else:
                unclaimed_readers.append(file_reader)
        return (unclaimed_readers, vcf_readers)

def claim ( self , file_readers ) : unclaimed_readers = [ ] vcf_readers = [ ] for file_reader in file_readers : if self . _is_mutect_vcf ( file_reader ) : vcf_reader = vcf . VcfReader ( file_reader ) vcf_readers . append ( _MutectVcfReader ( vcf_reader ) ) else : unclaimed_readers . append ( file_reader ) return ( unclaimed_readers , vcf_readers )

Recognizes and claims MuTect VCFs form the set of all input VCFs .


def _get_new_column_header(self, vcf_reader):
        """Returns a standardized column header.

        MuTect sample headers include the name of input alignment, which is
        nice, but doesn't match up with the sample names reported in Strelka
        or VarScan. To fix this, we replace with NORMAL and TUMOR using the
        MuTect metadata command line to replace them correctly."""
        mutect_dict = self._build_mutect_dict(vcf_reader.metaheaders)

        new_header_list = []
        required_keys = set([self._NORMAL_SAMPLE_KEY, self._TUMOR_SAMPLE_KEY])
        mutect_keys = set(mutect_dict.keys())

        if not required_keys.issubset(mutect_keys):
            raise utils.JQException("Unable to determine normal "
                                    "and tumor sample ordering "
                                    "based on MuTect metaheader.")

        for field_name in vcf_reader.column_header.split("\t"):
            if field_name == mutect_dict[self._NORMAL_SAMPLE_KEY]:
                field_name = "NORMAL"
            elif field_name == mutect_dict[self._TUMOR_SAMPLE_KEY]:
                field_name = "TUMOR"
            new_header_list.append(field_name)

        return "\t".join(new_header_list)

def
_get_new_column_header
(
self
,
vcf_reader
)
:
mutect_dict
=
self
.
_build_mutect_dict
(
vcf_reader
.
metaheaders
)
new_header_list
=
[
]
required_keys
=
set
(
[
self
.
_NORMAL_SAMPLE_KEY
,
self
.
_TUMOR_SAMPLE_KEY
]
)
mutect_keys
=
set
(
mutect_dict
.
keys
(
)
)
if
not
required_keys
.
issubset
(
mutect_keys
)
:
raise
utils
.
JQException
(
"Unable to determine normal "
"and tumor sample ordering "
"based on MuTect metaheader."
)
for
field_name
in
vcf_reader
.
column_header
.
split
(
"\t"
)
:
if
field_name
==
mutect_dict
[
self
.
_NORMAL_SAMPLE_KEY
]
:
field_name
=
"NORMAL"
elif
field_name
==
mutect_dict
[
self
.
_TUMOR_SAMPLE_KEY
]
:
field_name
=
"TUMOR"
new_header_list
.
append
(
field_name
)
return
"\t"
.
join
(
new_header_list
)


def claim(self, file_readers):
        """Recognizes and claims VarScan VCFs form the set of all input VCFs.

        Each defined caller has a chance to evaluate and claim all the incoming
        files as something that it can process. Since VarScan can claim
        high-confidence files as well, this process is significantly more
        complex than for other callers.

        Args:
            file_readers: the collection of currently unclaimed files

        Returns:
            A tuple of unclaimed readers and VarScanVcfReaders.
        """

        (prefix_to_readers,
         filter_files,
         unclaimed_set) = self._find_varscan_files(file_readers)

        prefix_by_patients = self._split_prefix_by_patient(prefix_to_readers)
        self._validate_vcf_readers(prefix_by_patients)
        vcf_hc_pairs = self._pair_files(prefix_to_readers, filter_files)
        self._validate_vcf_hc_pairs(vcf_hc_pairs)
        vcf_readers = self._create_vcf_readers(vcf_hc_pairs)

        return list(unclaimed_set), vcf_readers

def claim ( self , file_readers ) : ( prefix_to_readers , filter_files , unclaimed_set ) = self . _find_varscan_files ( file_readers ) prefix_by_patients = self . _split_prefix_by_patient ( prefix_to_readers ) self . _validate_vcf_readers ( prefix_by_patients ) vcf_hc_pairs = self . _pair_files ( prefix_to_readers , filter_files ) self . _validate_vcf_hc_pairs ( vcf_hc_pairs ) vcf_readers = self . _create_vcf_readers ( vcf_hc_pairs ) return list ( unclaimed_set ) , vcf_readers

Recognizes and claims VarScan VCFs form the set of all input VCFs .


def _init_population_stats(self, vcf_reader, dependent_tag_id):
        '''Derive mean and stdev.

        Adapted from online variance algorithm from Knuth, The Art of Computer
        Programming, volume 2

        Returns: mean and stdev when len(values) > 1, otherwise (None, None)
            Values rounded to _MAX_PRECISION to ameliorate discrepancies between
            python versions.'''
        #pylint: disable=invalid-name
        n = 0
        mean = 0
        M2 = 0
        try:
            vcf_reader.open()
            for vcf_record in vcf_reader.vcf_records():
                for tag_values in vcf_record.sample_tag_values.values():
                    value = self._get_dependent_value(tag_values,
                                                      dependent_tag_id)
                    if value is not None:
                        n += 1
                        delta = value - mean
                        mean += delta / n
                        M2 += delta * (value - mean)
        finally:
            vcf_reader.close()

        mean = round(mean, self._MAX_PRECISION)

        stdev = 0
        if n == 0:
            mean = None
            stdev = None
        elif n >= 2:
            variance = M2/n
            stdev = round(math.sqrt(variance), self._MAX_PRECISION)

        return mean, stdev

def
_init_population_stats
(
self
,
vcf_reader
,
dependent_tag_id
)
:
#pylint: disable=invalid-name
n
=
0
mean
=
0
M2
=
0
try
:
vcf_reader
.
open
(
)
for
vcf_record
in
vcf_reader
.
vcf_records
(
)
:
for
tag_values
in
vcf_record
.
sample_tag_values
.
values
(
)
:
value
=
self
.
_get_dependent_value
(
tag_values
,
dependent_tag_id
)
if
value
is
not
:
n
+=
1
delta
=
value
mean
mean
+=
delta
/
n
M2
+=
delta
*
(
value
mean
)
finally
:
vcf_reader
.
close
(
)
mean
=
round
(
mean
,
self
.
_MAX_PRECISION
)
stdev
=
0
if
n
==
0
:
mean
=
stdev
=
elif
n
>=
2
:
variance
=
M2
/
n
stdev
=
round
(
math
.
sqrt
(
variance
)
,
self
.
_MAX_PRECISION
)
return
mean
,
stdev


def claim(self, unclaimed_file_readers):
        """Allows each caller to claim incoming files as they are recognized.

        Args:
            unclaimed_file_readers: Usually, all files in the input dir.

        Returns:
            A tuple of unclaimed file readers and claimed VcfReaders. The
            presence of any unclaimed file readers could indicate stray files
            in the input dir.
        """
        claimed_vcf_readers = []
        for caller in self._callers:
            (unclaimed_file_readers,
             translated_vcf_readers) = caller.claim(unclaimed_file_readers)
            claimed_vcf_readers.extend(translated_vcf_readers)

        return unclaimed_file_readers, claimed_vcf_readers

def claim ( self , unclaimed_file_readers ) : claimed_vcf_readers = [ ] for caller in self . _callers : ( unclaimed_file_readers , translated_vcf_readers ) = caller . claim ( unclaimed_file_readers ) claimed_vcf_readers . extend ( translated_vcf_readers ) return unclaimed_file_readers , claimed_vcf_readers

Allows each caller to claim incoming files as they are recognized .


def splitlines(self, data):
        """
        Split data into lines where lines are separated by LINE_TERMINATORS.

        :param data: Any chunk of binary data.
        :return: List of lines without any characters at LINE_TERMINATORS.
        """
        return re.split(b'|'.join(self.LINE_TERMINATORS), data)

def splitlines ( self , data ) : return re . split ( b'|' . join ( self . LINE_TERMINATORS ) , data )

Split data into lines where lines are separated by LINE_TERMINATORS .


def prefix_line_terminator(self, data):
        """
        Return line terminator data begins with or None.
        """
        for t in self.LINE_TERMINATORS:
            if data.startswith(t):
                return t

        return None

def prefix_line_terminator ( self , data ) : for t in self . LINE_TERMINATORS : if data . startswith ( t ) : return t return None

Return line terminator data begins with or None .


def suffix_line_terminator(self, data):
        """
        Return line terminator data ends with or None.
        """
        for t in self.LINE_TERMINATORS:
            if data.endswith(t):
                return t

        return None

def suffix_line_terminator ( self , data ) : for t in self . LINE_TERMINATORS : if data . endswith ( t ) : return t return None

Return line terminator data ends with or None .


def seek_next_line(self):
        """
        Seek next line relative to the current file position.

        :return: Position of the line or -1 if next line was not found.
        """
        where = self.file.tell()
        offset = 0

        while True:
            data_len, data = self.read(self.read_size)
            data_where = 0

            if not data_len:
                break

            # Consider the following example: Foo\r | \nBar where " | " denotes current position,
            # 'Foo\r' is the read part and '\nBar' is the remaining part.
            # We should completely consume terminator "\r\n" by reading one extra byte.
            if b'\r\n' in self.LINE_TERMINATORS and data[-1] == b'\r'[0]:
                terminator_where = self.file.tell()
                terminator_len, terminator_data = self.read(1)

                if terminator_len and terminator_data[0] == b'\n'[0]:
                    data_len += 1
                    data += b'\n'
                else:
                    self.file.seek(terminator_where)

            while data_where < data_len:
                terminator = self.prefix_line_terminator(data[data_where:])
                if terminator:
                    self.file.seek(where + offset + data_where + len(terminator))
                    return self.file.tell()
                else:
                    data_where += 1

            offset += data_len
            self.file.seek(where + offset)

        return -1

Seek next line relative to the current file position .


def seek_previous_line(self):
        """
        Seek previous line relative to the current file position.

        :return: Position of the line or -1 if previous line was not found.
        """
        where = self.file.tell()
        offset = 0

        while True:
            if offset == where:
                break

            read_size = self.read_size if self.read_size <= where else where
            self.file.seek(where - offset - read_size, SEEK_SET)
            data_len, data = self.read(read_size)

            # Consider the following example: Foo\r | \nBar where " | " denotes current position,
            # '\nBar' is the read part and 'Foo\r' is the remaining part.
            # We should completely consume terminator "\r\n" by reading one extra byte.
            if b'\r\n' in self.LINE_TERMINATORS and data[0] == b'\n'[0]:
                terminator_where = self.file.tell()
                if terminator_where > data_len + 1:
                    self.file.seek(where - offset - data_len - 1, SEEK_SET)
                    terminator_len, terminator_data = self.read(1)

                    if terminator_data[0] == b'\r'[0]:
                        data_len += 1
                        data = b'\r' + data

                    self.file.seek(terminator_where)

            data_where = data_len

            while data_where > 0:
                terminator = self.suffix_line_terminator(data[:data_where])
                if terminator and offset == 0 and data_where == data_len:
                    # The last character is a line terminator that finishes current line. Ignore it.
                    data_where -= len(terminator)
                elif terminator:
                    self.file.seek(where - offset - (data_len - data_where))
                    return self.file.tell()
                else:
                    data_where -= 1

            offset += data_len

        if where == 0:
            # Nothing more to read.
            return -1
        else:
            # Very first line.
            self.file.seek(0)
            return 0

Seek previous line relative to the current file position .


def tail(self, lines=10):
        """
        Return the last lines of the file.
        """
        self.file.seek(0, SEEK_END)

        for i in range(lines):
            if self.seek_previous_line() == -1:
                break

        data = self.file.read()

        for t in self.LINE_TERMINATORS:
            if data.endswith(t):
                # Only terminators _between_ lines should be preserved.
                # Otherwise terminator of the last line will be treated as separtaing line and empty line.
                data = data[:-len(t)]
                break

        if data:
            return self.splitlines(data)
        else:
            return []


def head(self, lines=10):
        """
        Return the top lines of the file.
        """
        self.file.seek(0)

        for i in range(lines):
            if self.seek_next_line() == -1:
                break

        end_pos = self.file.tell()

        self.file.seek(0)
        data = self.file.read(end_pos)

        for t in self.LINE_TERMINATORS:
            if data.endswith(t):
                # Only terminators _between_ lines should be preserved.
                # Otherwise terminator of the last line will be treated as separtaing line and empty line.
                data = data[:-len(t)]
                break

        if data:
            return self.splitlines(data)
        else:
            return []


def follow(self):
        """
        Iterator generator that returns lines as data is added to the file.

        None will be yielded if no new line is available.
        Caller may either wait and re-try or end iteration.
        """
        trailing = True

        while True:
            where = self.file.tell()

            if where > os.fstat(self.file.fileno()).st_size:
                # File was truncated.
                where = 0
                self.file.seek(where)

            line = self.file.readline()

            if line:
                if trailing and line in self.LINE_TERMINATORS:
                    # This is just the line terminator added to the end of the file
                    # before a new line, ignore.
                    trailing = False
                    continue

                terminator = self.suffix_line_terminator(line)
                if terminator:
                    line = line[:-len(terminator)]

                trailing = False
                yield line
            else:
                trailing = True
                self.file.seek(where)
                yield None

Iterator generator that returns lines as data is added to the file .


def claim(self, file_readers):
        """Recognizes and claims Strelka VCFs form the set of all input VCFs.

        Each defined caller has a chance to evaluate and claim all the incoming
        files as something that it can process.

        Args:
            file_readers: the collection of currently unclaimed files

        Returns:
            A tuple of unclaimed readers and StrelkaVcfReaders.
        """
        (prefix_to_reader,
         unclaimed_readers) = self._find_strelka_files(file_readers)
        prefix_by_patients = self._split_prefix_by_patient(prefix_to_reader)
        self._validate_vcf_readers(prefix_by_patients)
        vcf_readers = self._create_vcf_readers(prefix_to_reader)

        return (unclaimed_readers, vcf_readers)

def claim ( self , file_readers ) : ( prefix_to_reader , unclaimed_readers ) = self . _find_strelka_files ( file_readers ) prefix_by_patients = self . _split_prefix_by_patient ( prefix_to_reader ) self . _validate_vcf_readers ( prefix_by_patients ) vcf_readers = self . _create_vcf_readers ( prefix_to_reader ) return ( unclaimed_readers , vcf_readers )

Recognizes and claims Strelka VCFs form the set of all input VCFs .


def parse_record(cls, vcf_line, sample_names):
        """Alternative constructor that parses VcfRecord from VCF string.

        Aspire to parse/represent the data such that it could be reliably
        round-tripped. (This nicety means INFO fields and FORMAT tags should be
        treated as ordered to avoid shuffling.)

        Args:
            vcf_line: the VCF variant record as a string; tab separated fields,
                trailing newlines are ignored. Must have at least 8 fixed fields
                (through INFO)
            sample_names: a list of sample name strings; these should match
                the VCF header column
        Returns:
            A mutable VcfRecord.
        """
        vcf_fields = vcf_line.rstrip("\r\n").split("\t")
        chrom, pos, rid, ref, alt, qual, rfilter, info \
                = vcf_fields[0:8]
        sample_fields = []
        sample_tag_values = {}
        if len(vcf_fields) > 9:
            rformat = vcf_fields[8]
            sample_fields = vcf_fields[9:]
            sample_tag_values = VcfRecord._sample_tag_values(sample_names,
                                                             rformat,
                                                             sample_fields)
        return VcfRecord(chrom, pos, ref, alt,
                         rid, qual, rfilter, info,
                         sample_tag_values)

def parse_record ( cls , vcf_line , sample_names ) : vcf_fields = vcf_line . rstrip ( "\r\n" ) . split ( "\t" ) chrom , pos , rid , ref , alt , qual , rfilter , info = vcf_fields [ 0 : 8 ] sample_fields = [ ] sample_tag_values = { } if len ( vcf_fields ) > 9 : rformat = vcf_fields [ 8 ] sample_fields = vcf_fields [ 9 : ] sample_tag_values = VcfRecord . _sample_tag_values ( sample_names , rformat , sample_fields ) return VcfRecord ( chrom , pos , ref , alt , rid , qual , rfilter , info , sample_tag_values )

Alternative constructor that parses VcfRecord from VCF string .


def _sample_tag_values(cls, sample_names, rformat, sample_fields):
        """Creates a sample dict of tag-value dicts for a single variant record.

        Args:
            sample_names: list of sample name strings.
            rformat: record format string (from VCF record).
            sample_fields: list of strings where each string is the ';'
                seperated format values for an individual sample.

        Returns:
            An dict of samples, where each key is a sample and each value
                is an dict of format-values. See attribute below for example.
                Will return '.' if no values for sampe field.
        """
        sample_tag_values = OrderedDict()
        tag_names = VcfRecord._format_list(rformat)
        for i, sample_field in enumerate(sample_fields):
            tag_values = sample_field.split(":") if sample_field else "."
            sample_tag_values[sample_names[i]] = OrderedDict(zip(tag_names,
                                                                 tag_values))
        return sample_tag_values

def _sample_tag_values ( cls , sample_names , rformat , sample_fields ) : sample_tag_values = OrderedDict ( ) tag_names = VcfRecord . _format_list ( rformat ) for i , sample_field in enumerate ( sample_fields ) : tag_values = sample_field . split ( ":" ) if sample_field else "." sample_tag_values [ sample_names [ i ] ] = OrderedDict ( zip ( tag_names , tag_values ) ) return sample_tag_values

Creates a sample dict of tag - value dicts for a single variant record .


def format_tags(self):
        """Returns set of format tags."""
        tags = VcfRecord._EMPTY_SET
        if self.sample_tag_values:
            first_sample = list(self.sample_tag_values.keys())[0]
            tags = set(self.sample_tag_values[first_sample].keys())
        return tags

def format_tags ( self ) : tags = VcfRecord . _EMPTY_SET if self . sample_tag_values : first_sample = list ( self . sample_tag_values . keys ( ) ) [ 0 ] tags = set ( self . sample_tag_values [ first_sample ] . keys ( ) ) return tags


def _join_info_fields(self):
        """Updates info attribute from info dict."""
        if self.info_dict:
            info_fields = []
            if len(self.info_dict) > 1:
                self.info_dict.pop(".", None)
            for field, value in self.info_dict.items():
                if field == value:
                    info_fields.append(value)
                else:
                    info_fields.append("=".join([field, value]))
            self.info = ";".join(info_fields)
        else:
            self.info = "."

def _join_info_fields ( self ) : if self . info_dict : info_fields = [ ] if len ( self . info_dict ) > 1 : self . info_dict . pop ( "." , None ) for field , value in self . info_dict . items ( ) : if field == value : info_fields . append ( value ) else : info_fields . append ( "=" . join ( [ field , value ] ) ) self . info = ";" . join ( info_fields ) else : self . info = "."


def _format_field(self):
        """Returns string representation of format field."""
        format_field = "."
        if self.sample_tag_values:
            first_sample = list(self.sample_tag_values.keys())[0]
            tag_names = self.sample_tag_values[first_sample].keys()
            if tag_names:
                format_field = ":".join(tag_names)
        return format_field

def _format_field ( self ) : format_field = "." if self . sample_tag_values : first_sample = list ( self . sample_tag_values . keys ( ) ) [ 0 ] tag_names = self . sample_tag_values [ first_sample ] . keys ( ) if tag_names : format_field = ":" . join ( tag_names ) return format_field

Returns string representation of format field .


def _sample_field(self, sample):
        """Returns string representation of sample-format values.

        Raises:
            KeyError: if requested sample is not defined.
        """
        tag_values = self.sample_tag_values[sample].values()
        if tag_values:
            return ":".join(tag_values)
        else:
            return "."

def _sample_field ( self , sample ) : tag_values = self . sample_tag_values [ sample ] . values ( ) if tag_values : return ":" . join ( tag_values ) else : return "."

Returns string representation of sample - format values .


def text(self):
        "Returns tab-delimited, newline terminated string of VcfRecord."
        stringifier = [self.chrom, self.pos, self.vcf_id, self.ref, self.alt,
                       self.qual, self.filter, self.info,
                       self._format_field()]

        for sample in self.sample_tag_values:
            stringifier.append(self._sample_field(sample))

        return "\t".join(stringifier) + "\n"

def text ( self ) : stringifier = [ self . chrom , self . pos , self . vcf_id , self . ref , self . alt , self . qual , self . filter , self . info , self . _format_field ( ) ] for sample in self . sample_tag_values : stringifier . append ( self . _sample_field ( sample ) ) return "\t" . join ( stringifier ) + "\n"

Returns tab - delimited newline terminated string of VcfRecord .


def add_sample_tag_value(self, tag_name, new_sample_values):
        """Appends a new format tag-value for all samples.

        Args:
            tag_name: string tag name; must not already exist
            new_sample

        Raises:
            KeyError: if tag_name to be added already exists
        """
        if tag_name in self.format_tags:
            msg = "New format value [{}] already exists.".format(tag_name)
            raise KeyError(msg)

        if not self._samples_match(new_sample_values):
            raise KeyError("Sample name values must match "
                           "existing sample names")
        for sample in self.sample_tag_values.keys():
            value = str(new_sample_values[sample])
            self.sample_tag_values[sample][tag_name] = value

def
add_sample_tag_value
(
self
,
tag_name
,
new_sample_values
)
:
if
tag_name
in
self
.
format_tags
:
msg
=
"New format value [{}] already exists."
.
format
(
tag_name
)
raise
KeyError
(
msg
)
if
not
self
.
_samples_match
(
new_sample_values
)
:
raise
KeyError
(
"Sample name values must match "
"existing sample names"
)
for
sample
in
self
.
sample_tag_values
.
keys
(
)
:
value
=
str
(
new_sample_values
[
sample
]
)
self
.
sample_tag_values
[
sample
]
[
tag_name
]
=
value

Appends a new format tag - value for all samples .


def add_or_replace_filter(self, new_filter):
        """Replaces null or blank filter or adds filter to existing list."""
        if self.filter.lower() in self._FILTERS_TO_REPLACE:
            self.filter = new_filter
        elif new_filter not in self.filter.split(";"):
            self.filter = ";".join([self.filter,
                                    new_filter])

def add_or_replace_filter ( self , new_filter ) : if self . filter . lower ( ) in self . _FILTERS_TO_REPLACE : self . filter = new_filter elif new_filter not in self . filter . split ( ";" ) : self . filter = ";" . join ( [ self . filter , new_filter ] )

Replaces null or blank filter or adds filter to existing list .


def available_categories(cls, user, products=AllProducts):
        ''' Returns the categories available to the user. Specify `products` if
        you want to restrict to just the categories that hold the specified
        products, otherwise it'll do all. '''

        # STOPGAP -- this needs to be elsewhere tbqh
        from .product import ProductController

        if products is AllProducts:
            products = inventory.Product.objects.all().select_related(
                "category",
            )

        available = ProductController.available_products(
            user,
            products=products,
        )

        return sorted(set(i.category for i in available), key=attrgetter("order"))

Returns the categories available to the user . Specify products if you want to restrict to just the categories that hold the specified products otherwise it ll do all .


def ProductsForm(category, products):
    ''' Produces an appropriate _ProductsForm subclass for the given render
    type. '''

    # Each Category.RENDER_TYPE value has a subclass here.
    cat = inventory.Category
    RENDER_TYPES = {
        cat.RENDER_TYPE_QUANTITY: _QuantityBoxProductsForm,
        cat.RENDER_TYPE_RADIO: _RadioButtonProductsForm,
        cat.RENDER_TYPE_ITEM_QUANTITY: _ItemQuantityProductsForm,
        cat.RENDER_TYPE_CHECKBOX: _CheckboxProductsForm,
    }

    # Produce a subclass of _ProductsForm which we can alter the base_fields on
    class ProductsForm(RENDER_TYPES[category.render_type]):
        pass

    products = list(products)
    products.sort(key=lambda prod: prod.order)

    ProductsForm.set_fields(category, products)

    if category.render_type == inventory.Category.RENDER_TYPE_ITEM_QUANTITY:
        ProductsForm = forms.formset_factory(
            ProductsForm,
            formset=_ItemQuantityProductsFormSet,
        )

    return ProductsForm

Produces an appropriate _ProductsForm subclass for the given render type .


def staff_products_form_factory(user):
    ''' Creates a StaffProductsForm that restricts the available products to
    those that are available to a user. '''

    products = inventory.Product.objects.all()
    products = ProductController.available_products(user, products=products)

    product_ids = [product.id for product in products]
    product_set = inventory.Product.objects.filter(id__in=product_ids)

    class StaffProductsForm(forms.Form):
        ''' Form for allowing staff to add an item to a user's cart. '''

        product = forms.ModelChoiceField(
            widget=forms.Select,
            queryset=product_set,
        )

        quantity = forms.IntegerField(
            min_value=0,
        )

    return StaffProductsForm

Creates a StaffProductsForm that restricts the available products to those that are available to a user .


def add_product_error(self, product, error):
        ''' Adds an error to the given product's field '''

        ''' if product in field_names:
            field = field_names[product]
        elif isinstance(product, inventory.Product):
            return
        else:
            field = None '''

        self.add_error(self.field_name(product), error)

Adds an error to the given product s field


def memoise(cls, func):
        ''' Decorator that stores the result of the stored function in the
        user's results cache until the batch completes. Keyword arguments are
        not yet supported.

        Arguments:
            func (callable(*a)): The function whose results we want
                to store. The positional arguments, ``a``, are used as cache
                keys.

        Returns:
            callable(*a): The memosing version of ``func``.

        '''

        @functools.wraps(func)
        def f(*a):

            for arg in a:
                if isinstance(arg, User):
                    user = arg
                    break
            else:
                raise ValueError("One position argument must be a User")

            func_key = (func, tuple(a))
            cache = cls.get_cache(user)

            if func_key not in cache:
                cache[func_key] = func(*a)

            return cache[func_key]

        return f

def
memoise
(
cls
,
func
)
:
@
functools
.
wraps
(
func
)
def
f
(
*
a
)
:
for
arg
in
a
:
if
isinstance
(
arg
,
User
)
:
user
=
arg
break
else
:
raise
ValueError
(
"One position argument must be a User"
)
func_key
=
(
func
,
tuple
(
a
)
)
cache
=
cls
.
get_cache
(
user
)
if
func_key
not
in
cache
:
cache
[
func_key
]
=
func
(
*
a
)
return
cache
[
func_key
]
return
f

Decorator that stores the result of the stored function in the user s results cache until the batch completes . Keyword arguments are not yet supported .


def model_fields_form_factory(model):
    ''' Creates a form for specifying fields from a model to display. '''

    fields = model._meta.get_fields()

    choices = []
    for field in fields:
        if hasattr(field, "verbose_name"):
            choices.append((field.name, field.verbose_name))

    class ModelFieldsForm(forms.Form):
        fields = forms.MultipleChoiceField(
            choices=choices,
            required=False,
        )

    return ModelFieldsForm

def model_fields_form_factory ( model ) : fields = model . _meta . get_fields ( ) choices = [ ] for field in fields : if hasattr ( field , "verbose_name" ) : choices . append ( ( field . name , field . verbose_name ) ) class ModelFieldsForm ( forms . Form ) : fields = forms . MultipleChoiceField ( choices = choices , required = False , ) return ModelFieldsForm

Creates a form for specifying fields from a model to display .


def items_pending_or_purchased(self):
        ''' Returns the items that this user has purchased or has pending. '''
        status = [commerce.Cart.STATUS_PAID, commerce.Cart.STATUS_ACTIVE]
        return self._items(status)

def items_pending_or_purchased ( self ) : status = [ commerce . Cart . STATUS_PAID , commerce . Cart . STATUS_ACTIVE ] return self . _items ( status )

Returns the items that this user has purchased or has pending .


def send_email(self, to, kind, **kwargs):
        ''' Sends an e-mail to the given address.

        to: The address
        kind: the ID for an e-mail kind; it should point to a subdirectory of
            self.template_prefix containing subject.txt and message.html, which
            are django templates for the subject and HTML message respectively.

        context: a context for rendering the e-mail.

        '''

        return __send_email__(self.template_prefix, to, kind, **kwargs)

def send_email ( self , to , kind , * * kwargs ) : return __send_email__ ( self . template_prefix , to , kind , * * kwargs )

Sends an e - mail to the given address .


def iter_osm_stream(start_sqn=None, base_url='https://planet.openstreetmap.org/replication/minute', expected_interval=60, parse_timestamps=True, state_dir=None):
    """Start processing an OSM diff stream and yield one changeset at a time to
    the caller."""

    # If the user specifies a state_dir, read the state from the statefile there
    if state_dir:
        if not os.path.exists(state_dir):
            raise Exception('Specified state_dir "%s" doesn\'t exist.' % state_dir)

        if os.path.exists('%s/state.txt' % state_dir):
            with open('%s/state.txt' % state_dir) as f:
                state = readState(f)
                start_sqn = state['sequenceNumber']

    # If no start_sqn, assume to start from the most recent diff
    if not start_sqn:
        u = urllib2.urlopen('%s/state.txt' % base_url)
        state = readState(u)
    else:
        sqnStr = str(start_sqn).zfill(9)
        u = urllib2.urlopen('%s/%s/%s/%s.state.txt' % (base_url, sqnStr[0:3], sqnStr[3:6], sqnStr[6:9]))
        state = readState(u)

    interval_fudge = 0.0

    while True:
        sqnStr = state['sequenceNumber'].zfill(9)
        url = '%s/%s/%s/%s.osc.gz' % (base_url, sqnStr[0:3], sqnStr[3:6], sqnStr[6:9])
        content = urllib2.urlopen(url)
        content = StringIO.StringIO(content.read())
        gzipper = gzip.GzipFile(fileobj=content)

        for a in iter_osm_change_file(gzipper, parse_timestamps):
            yield a

        # After parsing the OSC, check to see how much time is remaining
        stateTs = datetime.datetime.strptime(state['timestamp'], "%Y-%m-%dT%H:%M:%SZ")
        yield (None, model.Finished(state['sequenceNumber'], stateTs))

        nextTs = stateTs + datetime.timedelta(seconds=expected_interval + interval_fudge)
        if datetime.datetime.utcnow() < nextTs:
            timeToSleep = (nextTs - datetime.datetime.utcnow()).total_seconds()
        else:
            timeToSleep = 0.0
        time.sleep(timeToSleep)

        # Then try to fetch the next state file
        sqnStr = str(int(state['sequenceNumber']) + 1).zfill(9)
        url = '%s/%s/%s/%s.state.txt' % (base_url, sqnStr[0:3], sqnStr[3:6], sqnStr[6:9])
        delay = 1.0
        while True:
            try:
                u = urllib2.urlopen(url)
                interval_fudge -= (interval_fudge / 2.0)
                break
            except urllib2.HTTPError as e:
                if e.code == 404:
                    time.sleep(delay)
                    delay = min(delay * 2, 13)
                    interval_fudge += delay

        if state_dir:
            with open('%s/state.txt' % state_dir, 'w') as f:
                f.write(u.read())
            with open('%s/state.txt' % state_dir, 'r') as f:
                state = readState(f)
        else:
            state = readState(u)

Start processing an OSM diff stream and yield one changeset at a time to the caller .


def parse_osm_file(f, parse_timestamps=True):
    """Parse a file-like containing OSM XML into memory and return an object with
    the nodes, ways, and relations it contains. """

    nodes = []
    ways = []
    relations = []

    for p in iter_osm_file(f, parse_timestamps):

        if type(p) == model.Node:
            nodes.append(p)
        elif type(p) == model.Way:
            ways.append(p)
        elif type(p) == model.Relation:
            relations.append(p)

    return (nodes, ways, relations)

def parse_osm_file ( f , parse_timestamps = True ) : nodes = [ ] ways = [ ] relations = [ ] for p in iter_osm_file ( f , parse_timestamps ) : if type ( p ) == model . Node : nodes . append ( p ) elif type ( p ) == model . Way : ways . append ( p ) elif type ( p ) == model . Relation : relations . append ( p ) return ( nodes , ways , relations )

Parse a file - like containing OSM XML into memory and return an object with the nodes ways and relations it contains .


def iter_osm_notes(feed_limit=25, interval=60, parse_timestamps=True):
    """ Parses the global OSM Notes feed and yields as much Note information as possible. """

    last_seen_guid = None
    while True:
        u = urllib2.urlopen('https://www.openstreetmap.org/api/0.6/notes/feed?limit=%d' % feed_limit)

        tree = etree.parse(u)

        new_notes = []
        for note_item in tree.xpath('/rss/channel/item'):
            title = note_item.xpath('title')[0].text

            if title.startswith('new note ('):
                action = 'create'
            elif title.startswith('new comment ('):
                action = 'comment'
            elif title.startswith('closed note ('):
                action = 'close'

            # Note that (at least for now) the link and guid are the same in the feed.
            guid = note_item.xpath('link')[0].text

            if last_seen_guid == guid:
                break
            elif last_seen_guid == None:
                # The first time through we want the first item to be the "last seen"
                # because the RSS feed is newest-to-oldest
                last_seen_guid = guid
            else:
                note_id = int(guid.split('/')[-1].split('#c')[0])
                new_notes.append((action, get_note(note_id, parse_timestamps)))

        # We yield the reversed list because we want to yield in change order
        # (i.e. "oldest to most current")
        for note in reversed(new_notes):
            yield note

        yield model.Finished(None, None)

        time.sleep(interval)

Parses the global OSM Notes feed and yields as much Note information as possible .


def passes_filter(self, user):
        ''' Returns true if the condition passes the filter '''

        cls = type(self.condition)
        qs = cls.objects.filter(pk=self.condition.id)
        return self.condition in self.pre_filter(qs, user)

def passes_filter ( self , user ) : cls = type ( self . condition ) qs = cls . objects . filter ( pk = self . condition . id ) return self . condition in self . pre_filter ( qs , user )


def is_met(self, user, filtered=False):
        ''' Returns True if this flag condition is met, otherwise returns
        False. It determines if the condition is met by calling pre_filter
        with a queryset containing only self.condition. '''

        if filtered:
            return True  # Why query again?

        return self.passes_filter(user)

def
is_met
(
self
,
user
,
filtered
=
False
)
:
if
filtered
:
return
True
# Why query again?
return
self
.
passes_filter
(
user
)

Returns True if this flag condition is met otherwise returns False . It determines if the condition is met by calling pre_filter with a queryset containing only self . condition .


def user_quantity_remaining(self, user, filtered=True):
        ''' returns 0 if the date range is violated, otherwise, it will return
        the quantity remaining under the stock limit.

        The filter for this condition must add an annotation called "remainder"
        in order for this to work.
        '''

        if filtered:
            if hasattr(self.condition, "remainder"):
                return self.condition.remainder

        # Mark self.condition with a remainder
        qs = type(self.condition).objects.filter(pk=self.condition.id)
        qs = self.pre_filter(qs, user)

        if len(qs) > 0:
            return qs[0].remainder
        else:
            return 0

def
user_quantity_remaining
(
self
,
user
,
filtered
=
True
)
:
if
filtered
:
if
hasattr
(
self
.
condition
,
"remainder"
)
:
return
self
.
condition
.
remainder
# Mark self.condition with a remainder
qs
=
type
(
self
.
condition
)
.
objects
.
filter
(
pk
=
self
.
condition
.
id
)
qs
=
self
.
pre_filter
(
qs
,
user
)
if
len
(
qs
)
>
0
:
return
qs
[
0
]
.
remainder
else
:
return
0

returns 0 if the date range is violated otherwise it will return the quantity remaining under the stock limit .


def pre_filter(self, queryset, user):
        ''' Returns all of the items from queryset where the user has a
        product from a category invoking that item's condition in one of their
        carts. '''

        in_user_carts = Q(
            enabling_category__product__productitem__cart__user=user
        )
        released = commerce.Cart.STATUS_RELEASED
        in_released_carts = Q(
            enabling_category__product__productitem__cart__status=released
        )
        queryset = queryset.filter(in_user_carts)
        queryset = queryset.exclude(in_released_carts)

        return queryset

Returns all of the items from queryset where the user has a product from a category invoking that item s condition in one of their carts .


def pre_filter(self, queryset, user):
        ''' Returns all of the items from queryset where the user has a
        product invoking that item's condition in one of their carts. '''

        in_user_carts = Q(enabling_products__productitem__cart__user=user)
        released = commerce.Cart.STATUS_RELEASED
        paid = commerce.Cart.STATUS_PAID
        active = commerce.Cart.STATUS_ACTIVE
        in_released_carts = Q(
            enabling_products__productitem__cart__status=released
        )
        not_in_paid_or_active_carts = ~(
            Q(enabling_products__productitem__cart__status=paid) |
            Q(enabling_products__productitem__cart__status=active)
        )

        queryset = queryset.filter(in_user_carts)
        queryset = queryset.exclude(
            in_released_carts & not_in_paid_or_active_carts
        )

        return queryset

Returns all of the items from queryset where the user has a product invoking that item s condition in one of their carts .


def pre_filter(self, queryset, user):
        ''' Returns all of the items from queryset where the date falls into
        any specified range, but not yet where the stock limit is not yet
        reached.'''

        now = timezone.now()

        # Keep items with no start time, or start time not yet met.
        queryset = queryset.filter(Q(start_time=None) | Q(start_time__lte=now))
        queryset = queryset.filter(Q(end_time=None) | Q(end_time__gte=now))

        # Filter out items that have been reserved beyond the limits
        quantity_or_zero = self._calculate_quantities(user)

        remainder = Case(
            When(limit=None, then=Value(_BIG_QUANTITY)),
            default=F("limit") - Sum(quantity_or_zero),
        )

        queryset = queryset.annotate(remainder=remainder)
        queryset = queryset.filter(remainder__gt=0)

        return queryset

Returns all of the items from queryset where the date falls into any specified range but not yet where the stock limit is not yet reached .


def pre_filter(self, queryset, user):
        ''' Returns all of the items from queryset which are enabled by a user
        being a presenter or copresenter of a non-cancelled proposal. '''

        # Filter out cancelled proposals
        queryset = queryset.filter(
            proposal_kind__proposalbase__presentation__cancelled=False
        )

        u = user
        # User is a presenter
        user_is_presenter = Q(
            is_presenter=True,
            proposal_kind__proposalbase__presentation__speaker__user=u,
        )
        # User is a copresenter
        user_is_copresenter = Q(
            is_copresenter=True,
            proposal_kind__proposalbase__presentation__additional_speakers__user=u,  # NOQA
        )

        return queryset.filter(user_is_presenter | user_is_copresenter)

Returns all of the items from queryset which are enabled by a user being a presenter or copresenter of a non - cancelled proposal .


def pre_filter(self, conditions, user):
        ''' Returns all of the items from conditions which are enabled by a
        user being member of a Django Auth Group. '''

        return conditions.filter(group__in=user.groups.all())

def pre_filter ( self , conditions , user ) : return conditions . filter ( group__in = user . groups . all ( ) )

Returns all of the items from conditions which are enabled by a user being member of a Django Auth Group .


def _modifies_cart(func):
    ''' Decorator that makes the wrapped function raise ValidationError
    if we're doing something that could modify the cart.

    It also wraps the execution of this function in a database transaction,
    and marks the boundaries of a cart operations batch.
    '''

    @functools.wraps(func)
    def inner(self, *a, **k):
        self._fail_if_cart_is_not_active()
        with transaction.atomic():
            with BatchController.batch(self.cart.user):
                # Mark the version of self in the batch cache as modified
                memoised = self.for_user(self.cart.user)
                memoised._modified_by_batch = True
                return func(self, *a, **k)
    return inner

Decorator that makes the wrapped function raise ValidationError if we re doing something that could modify the cart .


def for_user(cls, user):
        ''' Returns the user's current cart, or creates a new cart
        if there isn't one ready yet. '''

        try:
            existing = commerce.Cart.objects.get(
                user=user,
                status=commerce.Cart.STATUS_ACTIVE,
            )
        except ObjectDoesNotExist:
            existing = commerce.Cart.objects.create(
                user=user,
                time_last_updated=timezone.now(),
                reservation_duration=datetime.timedelta(),
            )
        return cls(existing)

def for_user ( cls , user ) : try : existing = commerce . Cart . objects . get ( user = user , status = commerce . Cart . STATUS_ACTIVE , ) except ObjectDoesNotExist : existing = commerce . Cart . objects . create ( user = user , time_last_updated = timezone . now ( ) , reservation_duration = datetime . timedelta ( ) , ) return cls ( existing )

Returns the user s current cart or creates a new cart if there isn t one ready yet .


def _autoextend_reservation(self):
        ''' Updates the cart's time last updated value, which is used to
        determine whether the cart has reserved the items and discounts it
        holds. '''

        time = timezone.now()

        # Calculate the residual of the _old_ reservation duration
        # if it's greater than what's in the cart now, keep it.
        time_elapsed_since_updated = (time - self.cart.time_last_updated)
        residual = self.cart.reservation_duration - time_elapsed_since_updated

        reservations = [datetime.timedelta(0), residual]

        # If we have vouchers, we're entitled to an hour at minimum.
        if len(self.cart.vouchers.all()) >= 1:
            reservations.append(inventory.Voucher.RESERVATION_DURATION)

        # Else, it's the maximum of the included products
        items = commerce.ProductItem.objects.filter(cart=self.cart)
        agg = items.aggregate(Max("product__reservation_duration"))
        product_max = agg["product__reservation_duration__max"]

        if product_max is not None:
            reservations.append(product_max)

        self.cart.time_last_updated = time
        self.cart.reservation_duration = max(reservations)

Updates the cart s time last updated value which is used to determine whether the cart has reserved the items and discounts it holds .


def apply_voucher(self, voucher_code):
        ''' Applies the voucher with the given code to this cart. '''

        # Try and find the voucher
        voucher = inventory.Voucher.objects.get(code=voucher_code.upper())

        # Re-applying vouchers should be idempotent
        if voucher in self.cart.vouchers.all():
            return

        self._test_voucher(voucher)

        # If successful...
        self.cart.vouchers.add(voucher)

Applies the voucher with the given code to this cart .


def validate_cart(self):
        ''' Determines whether the status of the current cart is valid;
        this is normally called before generating or paying an invoice '''

        cart = self.cart
        user = self.cart.user
        errors = []

        try:
            self._test_vouchers(self.cart.vouchers.all())
        except ValidationError as ve:
            errors.append(ve)

        items = commerce.ProductItem.objects.filter(cart=cart)
        items = items.select_related("product", "product__category")

        product_quantities = list((i.product, i.quantity) for i in items)
        try:
            self._test_limits(product_quantities)
        except ValidationError as ve:
            self._append_errors(errors, ve)

        try:
            self._test_required_categories()
        except ValidationError as ve:
            self._append_errors(errors, ve)

        # Validate the discounts
        # TODO: refactor in terms of available_discounts
        # why aren't we doing that here?!

        #     def available_discounts(cls, user, categories, products):

        products = [i.product for i in items]
        discounts_with_quantity = DiscountController.available_discounts(
            user,
            [],
            products,
        )
        discounts = set(i.discount.id for i in discounts_with_quantity)

        discount_items = commerce.DiscountItem.objects.filter(cart=cart)
        for discount_item in discount_items:
            discount = discount_item.discount

            if discount.id not in discounts:
                errors.append(
                    ValidationError("Discounts are no longer available")
                )

        if errors:
            raise ValidationError(errors)

Determines whether the status of the current cart is valid ; this is normally called before generating or paying an invoice


def fix_simple_errors(self):
        ''' This attempts to fix the easy errors raised by ValidationError.
        This includes removing items from the cart that are no longer
        available, recalculating all of the discounts, and removing voucher
        codes that are no longer available. '''

        # Fix vouchers first (this affects available discounts)
        to_remove = []
        for voucher in self.cart.vouchers.all():
            try:
                self._test_voucher(voucher)
            except ValidationError:
                to_remove.append(voucher)

        for voucher in to_remove:
            self.cart.vouchers.remove(voucher)

        # Fix products and discounts
        items = commerce.ProductItem.objects.filter(cart=self.cart)
        items = items.select_related("product")
        products = set(i.product for i in items)
        available = set(ProductController.available_products(
            self.cart.user,
            products=products,
        ))

        not_available = products - available
        zeros = [(product, 0) for product in not_available]

        self.set_quantities(zeros)

This attempts to fix the easy errors raised by ValidationError . This includes removing items from the cart that are no longer available recalculating all of the discounts and removing voucher codes that are no longer available .


def _recalculate_discounts(self):
        ''' Calculates all of the discounts available for this product.'''

        # Delete the existing entries.
        commerce.DiscountItem.objects.filter(cart=self.cart).delete()

        # Order the products such that the most expensive ones are
        # processed first.
        product_items = self.cart.productitem_set.all().select_related(
            "product", "product__category"
        ).order_by("-product__price")

        products = [i.product for i in product_items]
        discounts = DiscountController.available_discounts(
            self.cart.user,
            [],
            products,
        )

        # The highest-value discounts will apply to the highest-value
        # products first, because of the order_by clause
        for item in product_items:
            self._add_discount(item.product, item.quantity, discounts)

Calculates all of the discounts available for this product .


def _add_discount(self, product, quantity, discounts):
        ''' Applies the best discounts on the given product, from the given
        discounts.'''

        def matches(discount):
            ''' Returns True if and only if the given discount apples to
            our product. '''
            if isinstance(discount.clause, conditions.DiscountForCategory):
                return discount.clause.category == product.category
            else:
                return discount.clause.product == product

        def value(discount):
            ''' Returns the value of this discount clause
            as applied to this product '''
            if discount.clause.percentage is not None:
                return discount.clause.percentage * product.price
            else:
                return discount.clause.price

        discounts = [i for i in discounts if matches(i)]
        discounts.sort(key=value)

        for candidate in reversed(discounts):
            if quantity == 0:
                break
            elif candidate.quantity == 0:
                # This discount clause has been exhausted by this cart
                continue

            # Get a provisional instance for this DiscountItem
            # with the quantity set to as much as we have in the cart
            discount_item = commerce.DiscountItem.objects.create(
                product=product,
                cart=self.cart,
                discount=candidate.discount,
                quantity=quantity,
            )

            # Truncate the quantity for this DiscountItem if we exceed quantity
            ours = discount_item.quantity
            allowed = candidate.quantity
            if ours > allowed:
                discount_item.quantity = allowed
                discount_item.save()
                # Update the remaining quantity.
                quantity = ours - allowed
            else:
                quantity = 0

            candidate.quantity -= discount_item.quantity

Applies the best discounts on the given product from the given discounts .


def report_view(title, form_type=None):
    ''' Decorator that converts a report view function into something that
    displays a Report.

    Arguments:
        title (str):
            The title of the report.
        form_type (Optional[forms.Form]):
            A form class that can make this report display things. If not
            supplied, no form will be displayed.

    '''

    # Create & return view
    def _report(view):
        report_view = ReportView(view, title, form_type)
        report_view = user_passes_test(views._staff_only)(report_view)
        report_view = wraps(view)(report_view)

        # Add this report to the list of reports.
        _all_report_views.append(report_view)

        return report_view

    return _report

Decorator that converts a report view function into something that displays a Report .


def rows(self, content_type):
        ''' Returns the data rows for the table. '''

        for row in self._data:
            yield [
                self.cell_text(content_type, i, cell)
                for i, cell in enumerate(row)
            ]

def rows ( self , content_type ) : for row in self . _data : yield [ self . cell_text ( content_type , i , cell ) for i , cell in enumerate ( row ) ]


def get_form(self, request):

        ''' Creates an instance of self.form_type using request.GET '''

        # Create a form instance
        if self.form_type is not None:
            form = self.form_type(request.GET)

            # Pre-validate it
            form.is_valid()
        else:
            form = None

        return form

def
get_form
(
self
,
request
)
:
# Create a form instance
if
self
.
form_type
is
not
:
form
=
self
.
form_type
(
request
.
GET
)
# Pre-validate it
form
.
is_valid
(
)
else
:
form
=
return
form

Creates an instance of self . form_type using request . GET


def render(self, data):
        ''' Renders the reports based on data.content_type's value.

        Arguments:
            data (ReportViewRequestData): The report data. data.content_type
                is used to determine how the reports are rendered.

        Returns:
            HTTPResponse: The rendered version of the report.

        '''
        renderers = {
            "text/csv": self._render_as_csv,
            "text/html": self._render_as_html,
            None: self._render_as_html,
        }
        render = renderers[data.content_type]
        return render(data)

def render ( self , data ) : renderers = { "text/csv" : self . _render_as_csv , "text/html" : self . _render_as_html , None : self . _render_as_html , } render = renderers [ data . content_type ] return render ( data )

Renders the reports based on data . content_type s value .


def reports_list(request):
    ''' Lists all of the reports currently available. '''

    reports = []

    for report in get_all_reports():
        reports.append({
            "name": report.__name__,
            "url": reverse(report),
            "description": report.__doc__,
        })

    reports.sort(key=lambda report: report["name"])

    ctx = {
        "reports": reports,
    }

    return render(request, "registrasion/reports_list.html", ctx)

def reports_list ( request ) : reports = [ ] for report in get_all_reports ( ) : reports . append ( { "name" : report . __name__ , "url" : reverse ( report ) , "description" : report . __doc__ , } ) reports . sort ( key = lambda report : report [ "name" ] ) ctx = { "reports" : reports , } return render ( request , "registrasion/reports_list.html" , ctx )

Lists all of the reports currently available .


def items_sold():
    ''' Summarises the items sold and discounts granted for a given set of
    products, or products from categories. '''

    data = None
    headings = None

    line_items = commerce.LineItem.objects.filter(
        invoice__status=commerce.Invoice.STATUS_PAID,
    ).select_related("invoice")

    line_items = line_items.order_by(
        # sqlite requires an order_by for .values() to work
        "-price", "description",
    ).values(
        "price", "description",
    ).annotate(
        total_quantity=Sum("quantity"),
    )

    headings = ["Description", "Quantity", "Price", "Total"]

    data = []
    total_income = 0
    for line in line_items:
        cost = line["total_quantity"] * line["price"]
        data.append([
            line["description"], line["total_quantity"],
            line["price"], cost,
        ])
        total_income += cost

    data.append([
        "(TOTAL)", "--", "--", total_income,
    ])

    return ListReport("Items sold", headings, data)

Summarises the items sold and discounts granted for a given set of products or products from categories .


def sales_payment_summary():
    ''' Summarises paid items and payments. '''

    def value_or_zero(aggregate, key):
        return aggregate[key] or 0

    def sum_amount(payment_set):
        a = payment_set.values("amount").aggregate(total=Sum("amount"))
        return value_or_zero(a, "total")

    headings = ["Category", "Total"]
    data = []

    # Summarise all sales made (= income.)
    sales = commerce.LineItem.objects.filter(
        invoice__status=commerce.Invoice.STATUS_PAID,
    ).values(
        "price", "quantity"
    ).aggregate(
        total=Sum(F("price") * F("quantity"), output_field=CURRENCY()),
    )
    sales = value_or_zero(sales, "total")

    all_payments = sum_amount(commerce.PaymentBase.objects.all())

    # Manual payments
    # Credit notes generated (total)
    # Payments made by credit note
    # Claimed credit notes

    all_credit_notes = 0 - sum_amount(commerce.CreditNote.objects.all())
    unclaimed_credit_notes = 0 - sum_amount(commerce.CreditNote.unclaimed())
    claimed_credit_notes = sum_amount(
        commerce.CreditNoteApplication.objects.all()
    )
    refunded_credit_notes = 0 - sum_amount(commerce.CreditNote.refunded())

    data.append(["Items on paid invoices", sales])
    data.append(["All payments", all_payments])
    data.append(["Sales - Payments ", sales - all_payments])
    data.append(["All credit notes", all_credit_notes])
    data.append(["Credit notes paid on invoices", claimed_credit_notes])
    data.append(["Credit notes refunded", refunded_credit_notes])
    data.append(["Unclaimed credit notes", unclaimed_credit_notes])
    data.append([
        "Credit notes - (claimed credit notes + unclaimed credit notes)",
        all_credit_notes - claimed_credit_notes -
        refunded_credit_notes - unclaimed_credit_notes
        ])

    return ListReport("Sales and Payments Summary", headings, data)


def payments():
    ''' Shows the history of payments into the system '''

    payments = commerce.PaymentBase.objects.all()
    return QuerysetReport(
        "Payments",
        ["invoice__id", "id", "reference", "amount"],
        payments,
        link_view=views.invoice,
    )

def payments ( ) : payments = commerce . PaymentBase . objects . all ( ) return QuerysetReport ( "Payments" , [ "invoice__id" , "id" , "reference" , "amount" ] , payments , link_view = views . invoice , )


def credit_note_refunds():
    ''' Shows all of the credit notes that have been generated. '''
    notes_refunded = commerce.CreditNote.refunded()
    return QuerysetReport(
        "Credit note refunds",
        ["id", "creditnoterefund__reference", "amount"],
        notes_refunded,
        link_view=views.credit_note,
    )

def
credit_note_refunds
(
)
:
notes_refunded
=
commerce
.
CreditNote
.
refunded
(
)
return
QuerysetReport
(
"Credit note refunds"
,
[
"id"
,
"creditnoterefund__reference"
,
"amount"
]
,
notes_refunded
,
link_view
=
views
.
credit_note
,
)

Shows all of the credit notes that have been generated .


def product_status(request, form):
    ''' Summarises the inventory status of the given items, grouping by
    invoice status. '''

    products = form.cleaned_data["product"]
    categories = form.cleaned_data["category"]

    items = commerce.ProductItem.objects.filter(
        Q(product__in=products) | Q(product__category__in=categories),
    ).select_related("cart", "product")

    items = group_by_cart_status(
        items,
        ["product__category__order", "product__order"],
        ["product", "product__category__name", "product__name"],
    )

    headings = [
        "Product", "Paid", "Reserved", "Unreserved", "Refunded",
    ]
    data = []

    for item in items:
        data.append([
            "%s - %s" % (
                item["product__category__name"], item["product__name"]
            ),
            item["total_paid"],
            item["total_reserved"],
            item["total_unreserved"],
            item["total_refunded"],
        ])

    return ListReport("Inventory", headings, data)

def
product_status
(
request
,
form
)
:
products
=
form
.
cleaned_data
[
"product"
]
categories
=
form
.
cleaned_data
[
"category"
]
items
=
commerce
.
ProductItem
.
objects
.
filter
(
Q
(
product__in
=
products
)
|
Q
(
product__category__in
=
categories
)
,
)
.
select_related
(
"cart"
,
"product"
)
items
=
group_by_cart_status
(
items
,
[
"product__category__order"
,
"product__order"
]
,
[
"product"
,
"product__category__name"
,
"product__name"
]
,
)
headings
=
[
"Product"
,
"Paid"
,
"Reserved"
,
"Unreserved"
,
"Refunded"
,
]
data
=
[
]
for
item
in
items
:
data
.
append
(
[
"%s - %s"
%
(
item
[
"product__category__name"
]
,
item
[
"product__name"
]
)
,
item
[
"total_paid"
]
,
item
[
"total_reserved"
]
,
item
[
"total_unreserved"
]
,
item
[
"total_refunded"
]
,
]
)
return
ListReport
(
"Inventory"
,
headings
,
data
)

Summarises the inventory status of the given items grouping by invoice status .


def discount_status(request, form):
    ''' Summarises the usage of a given discount. '''

    discounts = form.cleaned_data["discount"]

    items = commerce.DiscountItem.objects.filter(
        Q(discount__in=discounts),
    ).select_related("cart", "product", "product__category")

    items = group_by_cart_status(
        items,
        ["discount"],
        ["discount", "discount__description"],
    )

    headings = [
        "Discount", "Paid", "Reserved", "Unreserved", "Refunded",
    ]
    data = []

    for item in items:
        data.append([
            item["discount__description"],
            item["total_paid"],
            item["total_reserved"],
            item["total_unreserved"],
            item["total_refunded"],
        ])

    return ListReport("Usage by item", headings, data)

def
discount_status
(
request
,
form
)
:
discounts
=
form
.
cleaned_data
[
"discount"
]
items
=
commerce
.
DiscountItem
.
objects
.
filter
(
Q
(
discount__in
=
discounts
)
,
)
.
select_related
(
"cart"
,
"product"
,
"product__category"
)
items
=
group_by_cart_status
(
items
,
[
"discount"
]
,
[
"discount"
,
"discount__description"
]
,
)
headings
=
[
"Discount"
,
"Paid"
,
"Reserved"
,
"Unreserved"
,
"Refunded"
,
]
data
=
[
]
for
item
in
items
:
data
.
append
(
[
item
[
"discount__description"
]
,
item
[
"total_paid"
]
,
item
[
"total_reserved"
]
,
item
[
"total_unreserved"
]
,
item
[
"total_refunded"
]
,
]
)
return
ListReport
(
"Usage by item"
,
headings
,
data
)

Summarises the usage of a given discount .


def product_line_items(request, form):
    ''' Shows each product line item from invoices, including their date and
    purchashing customer. '''

    products = form.cleaned_data["product"]
    categories = form.cleaned_data["category"]

    invoices = commerce.Invoice.objects.filter(
        (
            Q(lineitem__product__in=products) |
            Q(lineitem__product__category__in=categories)
        ),
        status=commerce.Invoice.STATUS_PAID,
    ).select_related(
        "cart",
        "user",
        "user__attendee",
        "user__attendee__attendeeprofilebase"
    ).order_by("issue_time")

    headings = [
        'Invoice', 'Invoice Date', 'Attendee', 'Qty', 'Product', 'Status'
    ]

    data = []
    for invoice in invoices:
        for item in invoice.cart.productitem_set.all():
            if item.product in products or item.product.category in categories:
                output = []
                output.append(invoice.id)
                output.append(invoice.issue_time.strftime('%Y-%m-%d %H:%M:%S'))
                output.append(
                    invoice.user.attendee.attendeeprofilebase.attendee_name()
                )
                output.append(item.quantity)
                output.append(item.product)
                cart = invoice.cart
                if cart.status == commerce.Cart.STATUS_PAID:
                    output.append('PAID')
                elif cart.status == commerce.Cart.STATUS_ACTIVE:
                    output.append('UNPAID')
                elif cart.status == commerce.Cart.STATUS_RELEASED:
                    output.append('REFUNDED')
                data.append(output)

    return ListReport("Line Items", headings, data)

def
product_line_items
(
request
,
form
)
:
products
=
form
.
cleaned_data
[
"product"
]
categories
=
form
.
cleaned_data
[
"category"
]
invoices
=
commerce
.
Invoice
.
objects
.
filter
(
(
Q
(
lineitem__product__in
=
products
)
|
Q
(
lineitem__product__category__in
=
categories
)
)
,
status
=
commerce
.
Invoice
.
STATUS_PAID
,
)
.
select_related
(
"cart"
,
"user"
,
"user__attendee"
,
"user__attendee__attendeeprofilebase"
)
.
order_by
(
"issue_time"
)
headings
=
[
'Invoice'
,
'Invoice Date'
,
'Attendee'
,
'Qty'
,
'Product'
,
'Status'
]
data
=
[
]
for
invoice
in
invoices
:
for
item
in
invoice
.
cart
.
productitem_set
.
all
(
)
:
if
item
.
product
in
products
or
item
.
product
.
category
in
categories
:
output
=
[
]
output
.
append
(
invoice
.
id
)
output
.
append
(
invoice
.
issue_time
.
strftime
(
'%Y-%m-%d %H:%M:%S'
)
)
output
.
append
(
invoice
.
user
.
attendee
.
attendeeprofilebase
.
attendee_name
(
)
)
output
.
append
(
item
.
quantity
)
output
.
append
(
item
.
product
)
cart
=
invoice
.
cart
if
cart
.
status
==
commerce
.
Cart
.
STATUS_PAID
:
output
.
append
(
'PAID'
)
elif
cart
.
status
==
commerce
.
Cart
.
STATUS_ACTIVE
:
output
.
append
(
'UNPAID'
)
elif
cart
.
status
==
commerce
.
Cart
.
STATUS_RELEASED
:
output
.
append
(
'REFUNDED'
)
data
.
append
(
output
)
return
ListReport
(
"Line Items"
,
headings
,
data
)

Shows each product line item from invoices including their date and purchashing customer .


def paid_invoices_by_date(request, form):
    ''' Shows the number of paid invoices containing given products or
    categories per day. '''

    products = form.cleaned_data["product"]
    categories = form.cleaned_data["category"]

    invoices = commerce.Invoice.objects.filter(
        (
            Q(lineitem__product__in=products) |
            Q(lineitem__product__category__in=categories)
        ),
        status=commerce.Invoice.STATUS_PAID,
    )

    # Invoices with payments will be paid at the time of their latest payment
    payments = commerce.PaymentBase.objects.all()
    payments = payments.filter(
        invoice__in=invoices,
    )
    payments = payments.order_by("invoice")
    invoice_max_time = payments.values("invoice").annotate(
        max_time=Max("time")
    )

    # Zero-value invoices will have no payments, so they're paid at issue time
    zero_value_invoices = invoices.filter(value=0)

    times = itertools.chain(
        (line["max_time"] for line in invoice_max_time),
        (invoice.issue_time for invoice in zero_value_invoices),
    )

    by_date = collections.defaultdict(int)
    for time in times:
        date = datetime.datetime(
            year=time.year, month=time.month, day=time.day
        )
        by_date[date] += 1

    data = [(date_, count) for date_, count in sorted(by_date.items())]
    data = [(date_.strftime("%Y-%m-%d"), count) for date_, count in data]

    return ListReport(
        "Paid Invoices By Date",
        ["date", "count"],
        data,
    )

Shows the number of paid invoices containing given products or categories per day .


def credit_notes(request, form):
    ''' Shows all of the credit notes in the system. '''

    notes = commerce.CreditNote.objects.all().select_related(
        "creditnoterefund",
        "creditnoteapplication",
        "invoice",
        "invoice__user__attendee__attendeeprofilebase",
    )

    return QuerysetReport(
        "Credit Notes",
        ["id",
         "invoice__user__attendee__attendeeprofilebase__invoice_recipient",
         "status", "value"],
        notes,
        headings=["id", "Owner", "Status", "Value"],
        link_view=views.credit_note,
    )

Shows all of the credit notes in the system .


def invoices(request, form):
    ''' Shows all of the invoices in the system. '''

    invoices = commerce.Invoice.objects.all().order_by("status", "id")

    return QuerysetReport(
        "Invoices",
        ["id", "recipient", "value", "get_status_display"],
        invoices,
        headings=["id", "Recipient", "Value", "Status"],
        link_view=views.invoice,
    )

def invoices ( request , form ) : invoices = commerce . Invoice . objects . all ( ) . order_by ( "status" , "id" ) return QuerysetReport ( "Invoices" , [ "id" , "recipient" , "value" , "get_status_display" ] , invoices , headings = [ "id" , "Recipient" , "Value" , "Status" ] , link_view = views . invoice , )

Shows all of the invoices in the system .


def attendee_list(request):
    ''' Returns a list of all attendees. '''

    attendees = people.Attendee.objects.select_related(
        "attendeeprofilebase",
        "user",
    )

    profiles = AttendeeProfile.objects.filter(
        attendee__in=attendees
    ).select_related(
        "attendee", "attendee__user",
    )
    profiles_by_attendee = dict((i.attendee, i) for i in profiles)

    attendees = attendees.annotate(
        has_registered=Count(
            Q(user__invoice__status=commerce.Invoice.STATUS_PAID)
        ),
    )

    headings = [
        "User ID", "Name", "Email", "Has registered",
    ]

    data = []

    for a in attendees:
        data.append([
            a.user.id,
            (profiles_by_attendee[a].attendee_name()
                if a in profiles_by_attendee else ""),
            a.user.email,
            a.has_registered > 0,
        ])

    # Sort by whether they've registered, then ID.
    data.sort(key=lambda a: (-a[3], a[0]))

    return AttendeeListReport("Attendees", headings, data, link_view=attendee)


def speaker_registrations(request, form):
    ''' Shows registration status for speakers with a given proposal kind. '''

    kinds = form.cleaned_data["kind"]

    presentations = schedule_models.Presentation.objects.filter(
        proposal_base__kind__in=kinds,
    ).exclude(
        cancelled=True,
    )

    users = User.objects.filter(
        Q(speaker_profile__presentations__in=presentations) |
        Q(speaker_profile__copresentations__in=presentations)
    )

    paid_carts = commerce.Cart.objects.filter(status=commerce.Cart.STATUS_PAID)

    paid_carts = Case(
        When(cart__in=paid_carts, then=Value(1)),
        default=Value(0),
        output_field=models.IntegerField(),
    )
    users = users.annotate(paid_carts=Sum(paid_carts))
    users = users.order_by("paid_carts")

    return QuerysetReport(
        "Speaker Registration Status",
        ["id", "speaker_profile__name", "email", "paid_carts"],
        users,
        link_view=attendee,
    )

    return []

def
speaker_registrations
(
request
,
form
)
:
kinds
=
form
.
cleaned_data
[
"kind"
]
presentations
=
schedule_models
.
Presentation
.
objects
.
filter
(
proposal_base__kind__in
=
kinds
,
)
.
exclude
(
cancelled
=
True
,
)
users
=
User
.
objects
.
filter
(
Q
(
speaker_profile__presentations__in
=
presentations
)
|
Q
(
speaker_profile__copresentations__in
=
presentations
)
)
paid_carts
=
commerce
.
Cart
.
objects
.
filter
(
status
=
commerce
.
Cart
.
STATUS_PAID
)
paid_carts
=
Case
(
When
(
cart__in
=
paid_carts
,
then
=
Value
(
1
)
)
,
default
=
Value
(
0
)
,
output_field
=
models
.
IntegerField
(
)
,
)
users
=
users
.
annotate
(
paid_carts
=
Sum
(
paid_carts
)
)
users
=
users
.
order_by
(
"paid_carts"
)
return
QuerysetReport
(
"Speaker Registration Status"
,
[
"id"
,
"speaker_profile__name"
,
"email"
,
"paid_carts"
]
,
users
,
link_view
=
attendee
,
)
return
[
]

Shows registration status for speakers with a given proposal kind .


def manifest(request, form):
    '''
    Produces the registration manifest for people with the given product
    type.
    '''

    products = form.cleaned_data["product"]
    categories = form.cleaned_data["category"]

    line_items = (
        Q(lineitem__product__in=products) |
        Q(lineitem__product__category__in=categories)
    )

    invoices = commerce.Invoice.objects.filter(
        line_items,
        status=commerce.Invoice.STATUS_PAID,
    ).select_related(
        "cart",
        "user",
        "user__attendee",
        "user__attendee__attendeeprofilebase"
    )

    users = set(i.user for i in invoices)

    carts = commerce.Cart.objects.filter(
        user__in=users
    )

    items = commerce.ProductItem.objects.filter(
        cart__in=carts
    ).select_related(
        "product",
        "product__category",
        "cart",
        "cart__user",
        "cart__user__attendee",
        "cart__user__attendee__attendeeprofilebase"
    ).order_by("product__category__order", "product__order")

    users = {}

    for item in items:
        cart = item.cart
        if cart.user not in users:
            users[cart.user] = {"unpaid": [], "paid": [], "refunded": []}
        items = users[cart.user]
        if cart.status == commerce.Cart.STATUS_ACTIVE:
            items["unpaid"].append(item)
        elif cart.status == commerce.Cart.STATUS_PAID:
            items["paid"].append(item)
        elif cart.status == commerce.Cart.STATUS_RELEASED:
            items["refunded"].append(item)

    users_by_name = list(users.keys())
    users_by_name.sort(key=(
        lambda i: i.attendee.attendeeprofilebase.attendee_name().lower()
    ))

    headings = ["User ID", "Name", "Paid", "Unpaid", "Refunded"]

    def format_items(item_list):
        strings = [
            '%d x %s' % (item.quantity, str(item.product))
            for item in item_list
        ]
        return ", \n".join(strings)

    output = []
    for user in users_by_name:
        items = users[user]
        output.append([
            user.id,
            user.attendee.attendeeprofilebase.attendee_name(),
            format_items(items["paid"]),
            format_items(items["unpaid"]),
            format_items(items["refunded"]),
        ])

    return ListReport("Manifest", headings, output)

Produces the registration manifest for people with the given product type .


def missing_categories(context):
    ''' Adds the categories that the user does not currently have. '''
    user = user_for_context(context)
    categories_available = set(CategoryController.available_categories(user))
    items = ItemController(user).items_pending_or_purchased()

    categories_held = set()

    for product, quantity in items:
        categories_held.add(product.category)

    return categories_available - categories_held

def missing_categories ( context ) : user = user_for_context ( context ) categories_available = set ( CategoryController . available_categories ( user ) ) items = ItemController ( user ) . items_pending_or_purchased ( ) categories_held = set ( ) for product , quantity in items : categories_held . add ( product . category ) return categories_available - categories_held

Adds the categories that the user does not currently have .


def available_credit(context):
    ''' Calculates the sum of unclaimed credit from this user's credit notes.

    Returns:
        Decimal: the sum of the values of unclaimed credit notes for the
            current user.

    '''

    notes = commerce.CreditNote.unclaimed().filter(
        invoice__user=user_for_context(context),
    )
    ret = notes.values("amount").aggregate(Sum("amount"))["amount__sum"] or 0
    return 0 - ret

def available_credit ( context ) : notes = commerce . CreditNote . unclaimed ( ) . filter ( invoice__user = user_for_context ( context ) , ) ret = notes . values ( "amount" ) . aggregate ( Sum ( "amount" ) ) [ "amount__sum" ] or 0 return 0 - ret

Calculates the sum of unclaimed credit from this user s credit notes .


def sold_out_and_unregistered(context):
    ''' If the current user is unregistered, returns True if there are no
    products in the TICKET_PRODUCT_CATEGORY that are available to that user.

    If there *are* products available, the return False.

    If the current user *is* registered, then return None (it's not a
    pertinent question for people who already have a ticket).

    '''

    user = user_for_context(context)
    if hasattr(user, "attendee") and user.attendee.completed_registration:
        # This user has completed registration, and so we don't need to answer
        # whether they have sold out yet.

        # TODO: what if a user has got to the review phase?
        # currently that user will hit the review page, click "Check out and
        # pay", and that will fail. Probably good enough for now.

        return None

    ticket_category = settings.TICKET_PRODUCT_CATEGORY
    categories = available_categories(context)

    return ticket_category not in [cat.id for cat in categories]

If the current user is unregistered returns True if there are no products in the TICKET_PRODUCT_CATEGORY that are available to that user .


def guided_registration(request, page_number=None):
    ''' Goes through the registration process in order, making sure user sees
    all valid categories.

    The user must be logged in to see this view.

    Parameter:
        page_number:
            1) Profile form (and e-mail address?)
            2) Ticket type
            3) Remaining products
            4) Mark registration as complete

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

                {
                    "current_step": int(),  # The current step in the
                                            # registration
                    "sections": sections,   # A list of
                                            # GuidedRegistrationSections
                    "title": str(),         # The title of the page
                    "total_steps": int(),   # The total number of steps
                }

    '''

    PAGE_PROFILE = 1
    PAGE_TICKET = 2
    PAGE_PRODUCTS = 3
    PAGE_PRODUCTS_MAX = 4
    TOTAL_PAGES = 4

    ticket_category = inventory.Category.objects.get(
        id=settings.TICKET_PRODUCT_CATEGORY
    )
    cart = CartController.for_user(request.user)

    attendee = people.Attendee.get_instance(request.user)

    # This guided registration process is only for people who have
    # not completed registration (and has confusing behaviour if you go
    # back to it.)
    if attendee.completed_registration:
        return redirect(review)

    # Calculate the current maximum page number for this user.
    has_profile = hasattr(attendee, "attendeeprofilebase")
    if not has_profile:
        # If there's no profile, they have to go to the profile page.
        max_page = PAGE_PROFILE
        redirect_page = PAGE_PROFILE
    else:
        # We have a profile.
        # Do they have a ticket?
        products = inventory.Product.objects.filter(
            productitem__cart=cart.cart
        )
        products = products.filter(category=ticket_category)

        if products.count() == 0:
            # If no ticket, they can only see the profile or ticket page.
            max_page = PAGE_TICKET
            redirect_page = PAGE_TICKET
        else:
            # If there's a ticket, they should *see* the general products page#
            # but be able to go to the overflow page if needs be.
            max_page = PAGE_PRODUCTS_MAX
            redirect_page = PAGE_PRODUCTS

    if page_number is None or int(page_number) > max_page:
        return redirect("guided_registration", redirect_page)

    page_number = int(page_number)

    next_step = redirect("guided_registration", page_number + 1)

    with BatchController.batch(request.user):

        # This view doesn't work if the conference has sold out.
        available = ProductController.available_products(
            request.user, category=ticket_category
        )
        if not available:
            messages.error(request, "There are no more tickets available.")
            return redirect("dashboard")

        sections = []

        # Build up the list of sections
        if page_number == PAGE_PROFILE:
            # Profile bit
            title = "Attendee information"
            sections = _guided_registration_profile_and_voucher(request)
        elif page_number == PAGE_TICKET:
            # Select ticket
            title = "Select ticket type"
            sections = _guided_registration_products(
                request, GUIDED_MODE_TICKETS_ONLY
            )
        elif page_number == PAGE_PRODUCTS:
            # Select additional items
            title = "Additional items"
            sections = _guided_registration_products(
                request, GUIDED_MODE_ALL_ADDITIONAL
            )
        elif page_number == PAGE_PRODUCTS_MAX:
            # Items enabled by things on page 3 -- only shows things
            # that have not been marked as complete.
            title = "More additional items"
            sections = _guided_registration_products(
                request, GUIDED_MODE_EXCLUDE_COMPLETE
            )

        if not sections:
            # We've filled in every category
            attendee.completed_registration = True
            attendee.save()
            return redirect("review")

        if sections and request.method == "POST":
            for section in sections:
                if section.form.errors:
                    break
            else:
                # We've successfully processed everything
                return next_step

    data = {
        "current_step": page_number,
        "sections": sections,
        "title": title,
        "total_steps": TOTAL_PAGES,
    }
    return render(request, "registrasion/guided_registration.html", data)

Goes through the registration process in order making sure user sees all valid categories .


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

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 .