<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 Guillaume Pellerin
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://svn.parisson.org/telemeta/TelemetaLicense.
#
# Author: Guillaume Pellerin <yomguy@parisson.com>
#

import os
import sys
import csv
import logging
import datetime
from django.core.management import setup_environ
from django.core.files.base import ContentFile

class Logger:

    def __init__(self, file):
        self.logger = logging.getLogger('myapp')
        self.hdlr = logging.FileHandler(file)
        self.formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
        self.hdlr.setFormatter(self.formatter)
        self.logger.addHandler(self.hdlr)
        self.logger.setLevel(logging.INFO)

    def info(self, prefix, message):
        self.logger.info(' ' + prefix + ' : ' + message.decode('utf8'))

    def error(self, prefix, message):
        self.logger.error(prefix + ' : ' + message.decode('utf8'))

class TelemetaWavImport:

    def __init__(self, source_dir, log_file, pattern):
        from django.contrib.auth.models import User
        self.logger = Logger(log_file)
        self.source_dir = source_dir
        self.collections = os.listdir(self.source_dir)
        self.pattern = pattern
        self.user = User.objects.filter(username='admin')[0]

    def write_file(self, item, wav_file, overwrite=False):
        filename = wav_file.split(os.sep)[-1]
        if os.path.exists(wav_file):
            if not item.file or overwrite:
                f = open(wav_file, 'r')
                file_content = ContentFile(f.read())
                item.file.save(filename, file_content)
                f.close()
                item.set_revision(self.user)
            else:
                msg = item.code + ' : fichier ' + item.file.name + ' deja inscrit dans la base de donnees !'
                self.logger.error('item', msg)
        else:
            msg = item.code + ' : fichier audio ' + filename + ' inexistant dans le dossier !'
            self.logger.error('item', msg)

    def wav_import(self):
        from telemeta.models import MediaItem,  MediaCollection

        for collection in self.collections:
            collection_dir = self.source_dir + os.sep + collection
            collection_files = os.listdir(collection_dir)
            collections = []

            if not '/.' in collection_dir and self.pattern in collection_dir:
                collection_name = collection.split(os.sep)[-1]
                collections.append(collection_name)
                c = MediaCollection.objects.filter(code=collection_name)

                if not c and collection + '.csv' in collection_files:
                    msg = collection + ' collection NON présente dans la base de données, SORTIE '
                    self.logger.error(collection, msg)
                    sys.exit(msg)
                elif not c:
                    msg = 'collection NON présente dans la base de données, CREATION '
                    self.logger.info(collection, msg)
                    c = MediaCollection(code=collection_name)
                    c.save()
                    c.set_revision(self.user)
                else:
                    msg = 'collection présente dans la base de données, SELECTION'
                    self.logger.info(collection, msg)

        for collection in collections:
            collection_dir = self.source_dir + os.sep + collection
            collection_name = collection
            collection_files = os.listdir(collection_dir)
            msg = '************************ ' + collection + ' ******************************'
            self.logger.info(collection, msg[:70])
            overwrite = True
            csv_file = ''
            rows = {}

            if collection + '.csv' in collection_files:
                csv_file = self.source_dir + os.sep + collection + os.sep + collection + '.csv'
                csv_data = csv.reader(open(csv_file), delimiter=';')
                for row in csv_data:
                    rows[row[1]] = row[0]
                msg = collection + ' import du fichier CSV de la collection'
                self.logger.info(collection, msg[:70])
            else:
                msg = collection + ' pas de fichier CSV dans la collection'
                self.logger.info(collection, msg[:70])

            c = MediaCollection.objects.filter(code=collection_name)
            if not c:
                c = MediaCollection(code=collection_name)
                c.save()
                msg = ' collection NON présente dans la BDD, CREATION '
                self.logger.info(c.code, msg)
            else:
                c = c[0]
                msg = ' id = '+str(c.id)
                self.logger.info(c.code, msg)

            audio_files = []
            for file in collection_files:
                ext = ['WAV', 'wav']
                if file.split('.')[-1] in ext:
                    audio_files.append(file)

            audio_files.sort()
            nb_items = c.items.count()
            counter = 0

            for file in audio_files:
                code = file.split('.')[0]
                wav_file = self.source_dir + os.sep + collection + os.sep + file

                if len(audio_files) <= nb_items:
                    items = MediaItem.objects.filter(code=code)

                    if code in rows and not items:
                        old_ref = rows[code]
                        items = MediaItem.objects.filter(old_code=old_ref)

                    if items:
                        item = items[0]
                        msg = item.old_code + ' : Cas 1 ou 2 : id = ' + str(item.id)
                        self.logger.info('item', msg)
                        item.code = code
                        item.save()
                    else:
                        item = MediaItem(code=code, collection=c)
                        msg = item.code + ' : Cas 1 ou 2 : item NON présent dans la base de données, CREATION'
                        self.logger.info('item', msg)

                    self.write_file(item, wav_file, overwrite)

                elif nb_items == 1 and len(audio_files) > 1:
                    if counter == 0:
                        msg = code + ' : Cas 3a : item n°01 présent dans la base de données, PASSE'
                        self.logger.info('item', msg)
                    else:
                        item = MediaItem(code=code, collection=c)
                        msg = item.code + ' : Cas 3a : item NON présent dans la base de données, CREATION'
                        self.logger.info('item', msg)
                        self.write_file(item, wav_file, overwrite)

                elif nb_items > 1 and nb_items < len(audio_files):
                    msg = code + ' : Cas 3b : nb items < nb de fichiers audio, PAS de creation'
                    self.logger.info('item', msg)

                counter += 1

def print_usage(tool_name):
    print "Usage: "+tool_name+" <project_dir> <source_dir> <pattern> <log_file>"
    print "  project_dir: the directory of the Django project which hosts Telemeta"
    print "  source_dir: the directory containing the wav files to include"
    print "  pattern: a pattern to match the collection names"
    print "  log_file: a log file to write logs"

def run():
    if len(sys.argv) < 3:
        print_usage(os.path.basename(sys.argv[0]))
        sys.exit(1)
    else:
        project_dir = sys.argv[-4]
        source_dir = sys.argv[-3]
        pattern = sys.argv[-2]
        log_file = sys.argv[-1]
        sys.path.append(project_dir)
        import settings
        setup_environ(settings)
        t = TelemetaWavImport(source_dir, log_file, pattern)
        t.wav_import()

if __name__ == '__main__':
    run()
<commit_msg>import crem: bugfix<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 Guillaume Pellerin
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://svn.parisson.org/telemeta/TelemetaLicense.
#
# Author: Guillaume Pellerin <yomguy@parisson.com>
#

import os
import sys
import csv
import logging
import datetime
from django.core.management import setup_environ
from django.core.files.base import ContentFile

class Logger:

    def __init__(self, file):
        self.logger = logging.getLogger('myapp')
        self.hdlr = logging.FileHandler(file)
        self.formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
        self.hdlr.setFormatter(self.formatter)
        self.logger.addHandler(self.hdlr)
        self.logger.setLevel(logging.INFO)

    def info(self, prefix, message):
        self.logger.info(' ' + prefix + ' : ' + message.decode('utf8'))

    def error(self, prefix, message):
        self.logger.error(prefix + ' : ' + message.decode('utf8'))

class TelemetaWavImport:

    def __init__(self, source_dir, log_file, pattern):
        from django.contrib.auth.models import User
        self.logger = Logger(log_file)
        self.source_dir = source_dir
        self.collections = os.listdir(self.source_dir)
        self.pattern = pattern
        self.user = User.objects.filter(username='admin')[0]

    def write_file(self, item, wav_file, overwrite=False):
        filename = wav_file.split(os.sep)[-1]
        if os.path.exists(wav_file):
            if not item.file or overwrite:
                f = open(wav_file, 'r')
                file_content = ContentFile(f.read())
                item.file.save(filename, file_content)
                f.close()
                item.set_revision(self.user)
            else:
                msg = item.code + ' : fichier ' + item.file.name + ' deja inscrit dans la base de donnees !'
                self.logger.error('item', msg)
        else:
            msg = item.code + ' : fichier audio ' + filename + ' inexistant dans le dossier !'
            self.logger.error('item', msg)

    def wav_import(self):
        from telemeta.models import MediaItem,  MediaCollection

        collections = []
        for collection in self.collections:
            collection_dir = self.source_dir + os.sep + collection
            collection_files = os.listdir(collection_dir)

            if not '/.' in collection_dir and self.pattern in collection_dir:
                collection_name = collection.split(os.sep)[-1]
                collections.append(collection_name)
                c = MediaCollection.objects.filter(code=collection_name)

                if not c and collection + '.csv' in collection_files:
                    msg = collection + ' collection NON présente dans la base de données, SORTIE '
                    self.logger.error(collection, msg)
                    sys.exit(msg)
                elif not c:
                    msg = 'collection NON présente dans la base de données, CREATION '
                    self.logger.info(collection, msg)
                    c = MediaCollection(code=collection_name)
                    c.save()
                    c.set_revision(self.user)
                else:
                    msg = 'collection présente dans la base de données, SELECTION'
                    self.logger.info(collection, msg)

        for collection in collections:
            collection_dir = self.source_dir + os.sep + collection
            collection_name = collection
            collection_files = os.listdir(collection_dir)
            msg = '************************ ' + collection + ' ******************************'
            self.logger.info(collection, msg[:70])
            overwrite = True
            csv_file = ''
            rows = {}

            if collection + '.csv' in collection_files:
                csv_file = self.source_dir + os.sep + collection + os.sep + collection + '.csv'
                csv_data = csv.reader(open(csv_file), delimiter=';')
                for row in csv_data:
                    rows[row[1]] = row[0]
                msg = collection + ' import du fichier CSV de la collection'
                self.logger.info(collection, msg[:70])
            else:
                msg = collection + ' pas de fichier CSV dans la collection'
                self.logger.info(collection, msg[:70])

            c = MediaCollection.objects.filter(code=collection_name)
            if not c:
                c = MediaCollection(code=collection_name)
                c.save()
                msg = ' collection NON présente dans la BDD, CREATION '
                self.logger.info(c.code, msg)
            else:
                c = c[0]
                msg = ' id = '+str(c.id)
                self.logger.info(c.code, msg)

            audio_files = []
            for file in collection_files:
                ext = ['WAV', 'wav']
                if file.split('.')[-1] in ext:
                    audio_files.append(file)

            audio_files.sort()
            nb_items = c.items.count()
            counter = 0

            for file in audio_files:
                code = file.split('.')[0]
                wav_file = self.source_dir + os.sep + collection + os.sep + file

                if len(audio_files) <= nb_items:
                    items = MediaItem.objects.filter(code=code)

                    if code in rows and not items:
                        old_ref = rows[code]
                        items = MediaItem.objects.filter(old_code=old_ref)

                    if items:
                        item = items[0]
                        msg = item.old_code + ' : Cas 1 ou 2 : id = ' + str(item.id)
                        self.logger.info('item', msg)
                        item.code = code
                        item.save()
                    else:
                        item = MediaItem(code=code, collection=c)
                        msg = item.code + ' : Cas 1 ou 2 : item NON présent dans la base de données, CREATION'
                        self.logger.info('item', msg)

                    self.write_file(item, wav_file, overwrite)

                elif nb_items == 1 and len(audio_files) > 1:
                    if counter == 0:
                        msg = code + ' : Cas 3a : item n°01 présent dans la base de données, PASSE'
                        self.logger.info('item', msg)
                    else:
                        item = MediaItem(code=code, collection=c)
                        msg = item.code + ' : Cas 3a : item NON présent dans la base de données, CREATION'
                        self.logger.info('item', msg)
                        self.write_file(item, wav_file, overwrite)

                elif nb_items > 1 and nb_items < len(audio_files):
                    msg = code + ' : Cas 3b : nb items < nb de fichiers audio, PAS de creation'
                    self.logger.info('item', msg)

                counter += 1

def print_usage(tool_name):
    print "Usage: "+tool_name+" <project_dir> <source_dir> <pattern> <log_file>"
    print "  project_dir: the directory of the Django project which hosts Telemeta"
    print "  source_dir: the directory containing the wav files to include"
    print "  pattern: a pattern to match the collection names"
    print "  log_file: a log file to write logs"

def run():
    if len(sys.argv) < 3:
        print_usage(os.path.basename(sys.argv[0]))
        sys.exit(1)
    else:
        project_dir = sys.argv[-4]
        source_dir = sys.argv[-3]
        pattern = sys.argv[-2]
        log_file = sys.argv[-1]
        sys.path.append(project_dir)
        import settings
        setup_environ(settings)
        t = TelemetaWavImport(source_dir, log_file, pattern)
        t.wav_import()

if __name__ == '__main__':
    run()
<|endoftext|>


<commit_before>"""
When an admin submits a form to create/edit an attendee/group/job/etc we usually want to perform some basic validations
on the data that was entered.  We put those validations here.  To make a validation for the Attendee model, you can
just write a function decorated with the @validation.Attendee decorator.  That function should return None on success
and an error string on failure.

In addition, you can define a set of required fields by setting the .required field like the AdminAccount.required list
below.  This should be a list of tuples where the first tuple element is the name of the field, and the second is the
name that should be displayed in the "XXX is a required field" error message.

To perform these validations, call the "check" method on the instance you're validating.  That method returns None
on success and a string error message on validation failure.
"""
from uber.common import *

AdminAccount.required = [('attendee', 'Attendee'), ('hashed', 'Password')]

@validation.AdminAccount
def duplicate_admin(account):
    if account.is_new:
        with Session() as session:
            if session.query(AdminAccount).filter_by(attendee_id=account.attendee_id).all():
                return 'That attendee already has an admin account'

@validation.AdminAccount
def has_email_address(account):
    if account.is_new:
        with Session() as session:
            if session.query(Attendee).filter_by(id=account.attendee_id).first().email == '':
                return "Attendee doesn't have a valid email set"

Group.required = [('name', 'Group Name')]

@prereg_validation.Group
def dealer_wares(group):
    if group.tables and not group.wares:
        return "You must provide a detailed explanation of what you sell for us to evaluate your submission"

@prereg_validation.Group
def dealer_website(group):
    if group.tables and not group.website:
        return "Please enter your business' website address"

@prereg_validation.Group
def dealer_description(group):
    if group.tables and not group.description:
        return "Please provide a brief description of your business"

@prereg_validation.Group
def dealer_address(group):
    if group.tables and not group.address and not c.COLLECT_FULL_ADDRESS:
        "Please provide your full address for tax purposes"

@validation.Group
def group_paid(group):
    try:
        amount = int(float(group.amount_paid))
        if amount < 0:
            return 'Amount Paid must be a number that is 0 or higher.'
    except:
        return "What you entered for Amount Paid ({}) isn't even a number".format(group.amount_paid)

def _invalid_phone_number(s):
    if not s.startswith('+'):
        return len(re.findall(r'\d', s)) != 10 or re.search(c.SAME_NUMBER_REPEATED, re.sub(r'[^0-9]', '', s))

def _invalid_zip_code(s):
    return len(re.findall(r'\d', s)) not in [5, 9]

def ignore_unassigned_and_placeholders(func):
    @wraps(func)
    def with_skipping(attendee):
        unassigned_group_reg = attendee.group_id and not attendee.first_name and not attendee.last_name
        valid_placeholder = attendee.placeholder and attendee.first_name and attendee.last_name
        if not unassigned_group_reg and not valid_placeholder:
            return func(attendee)
    return with_skipping

@prereg_validation.Attendee
def dealer_cellphone(attendee):
    if attendee.badge_type == c.PSEUDO_DEALER_BADGE and not attendee.cellphone:
        return 'Your phone number is required'

@prereg_validation.Attendee
def shirt_size(attendee):
    if attendee.amount_extra >= c.SHIRT_LEVEL and attendee.shirt == c.NO_SHIRT:
        return 'Your shirt size is required'

@prereg_validation.Attendee
def total_cost_over_paid(attendee):
    if attendee.total_cost < attendee.amount_paid:
        return 'You have already paid ${}, you cannot reduce your extras below that.'.format(attendee.amount_paid)

@validation.Attendee
@ignore_unassigned_and_placeholders
def full_name(attendee):
    if not attendee.first_name:
        return 'First Name is a required field'
    elif not attendee.last_name:
        return 'Last Name is a required field'

@validation.Attendee
@ignore_unassigned_and_placeholders
def age(attendee):
    if c.COLLECT_EXACT_BIRTHDATE:
        if not attendee.birthdate:
            return 'Please enter a date of birth.'
        elif attendee.birthdate > date.today():
            return 'You cannot be born in the future.'
    elif not attendee.age_group:
        return 'Please enter your age group'

@validation.Attendee
@ignore_unassigned_and_placeholders
def address(attendee):
    if c.COLLECT_FULL_ADDRESS:
        if not attendee.address1:
            return 'Please enter a street address.'
        if not attendee.city:
            return 'Please enter a city.'
        if not attendee.region:
            return 'Please enter a state, province, or region.'
        if not attendee.country:
            return 'Please enter a country.'

@validation.Attendee
@ignore_unassigned_and_placeholders
def email(attendee):
    if len(attendee.email) > 255:
        return 'Email addresses cannot be longer than 255 characters.'

    if (c.AT_OR_POST_CON and attendee.email and not re.match(c.EMAIL_RE, attendee.email)) or (not c.AT_OR_POST_CON and not re.match(c.EMAIL_RE, attendee.email)):
        return 'Enter a valid email address'

@validation.Attendee
@ignore_unassigned_and_placeholders
def emergency_contact(attendee):
    if not attendee.international and _invalid_phone_number(attendee.ec_phone):
        if c.COLLECT_FULL_ADDRESS:
            return 'Enter a 10-digit US phone number or include a country code (e.g. +44).'
        else:
            return 'Enter a 10-digit emergency contact number'

@validation.Attendee
@ignore_unassigned_and_placeholders
def cellphone(attendee):
    if attendee.cellphone and _invalid_phone_number(attendee.cellphone):
        if c.COLLECT_FULL_ADDRESS:
            return 'Enter a 10-digit US phone number or include a country code (e.g. +44).'
        else:
            return 'Your cellphone number was not a valid 10-digit phone number'

    if not attendee.no_cellphone and attendee.staffing and not attendee.cellphone:
        return "Cellphone number is required for volunteers (unless you don't own a cellphone)"

@validation.Attendee
@ignore_unassigned_and_placeholders
def zip_code(attendee):
    if not attendee.international and not c.AT_OR_POST_CON:
        if _invalid_zip_code(attendee.zip_code):
            return 'Enter a valid zip code'

@validation.Attendee
def allowed_to_volunteer(attendee):
    if attendee.staffing and not attendee.age_group_conf['can_volunteer'] and attendee.badge_type != c.STAFF_BADGE and c.PRE_CON:
        return 'Volunteers cannot be ' + attendee.age_group_conf['desc']

@validation.Attendee
def allowed_to_register(attendee):
    if not attendee.age_group_conf['can_register']:
        return 'Attendees ' + attendee.age_group_conf['desc'] + ' years of age do not need to register, but MUST be accompanied by a parent at all times!'

@validation.Attendee
def printed_badge_deadline(attendee):
    if attendee.is_new and attendee.has_personalized_badge and not c.SHIFT_CUSTOM_BADGES:
        return 'Custom badges have already been ordered so you cannot create new {} badges'.format(attendee.badge_type_label)

@validation.Attendee
def group_leadership(attendee):
    if attendee.session and not attendee.group_id:
        orig_group_id = attendee.orig_value_of('group_id')
        if orig_group_id and attendee.id == attendee.session.group(orig_group_id).leader_id:
            return 'You cannot remove the leader of a group from that group; make someone else the leader first'

@validation.Attendee
def banned_volunteer(attendee):
    if (attendee.ribbon == c.VOLUNTEER_RIBBON or attendee.staffing) and attendee.full_name in c.BANNED_STAFFERS:
        return "We've declined to invite {} back as a volunteer, ".format(attendee.full_name) + (
                    'talk to Stops to override if necessary' if c.AT_THE_CON else
                    'Please contact us via {} if you believe this is in error'.format(c.CONTACT_URL))

@validation.Attendee
def attendee_money(attendee):
    try:
        amount_paid = int(float(attendee.amount_paid))
        if amount_paid < 0:
            return 'Amount Paid cannot be less than zero'
    except:
        return "What you entered for Amount Paid ({}) wasn't even a number".format(attendee.amount_paid)

    try:
        amount_extra = int(float(attendee.amount_extra or 0))
        if amount_extra < 0:
            return 'Amount extra must be a positive integer'
    except:
        return 'Invalid amount extra ({})'.format(attendee.amount_extra)

    if attendee.overridden_price is not None:
        try:
            overridden_price = int(float(attendee.overridden_price))
            if overridden_price < 0:
                return 'Overridden price must be a positive integer'
        except:
            return 'Invalid overridden price ({})'.format(attendee.overridden_price)
        else:
            if attendee.overridden_price == 0:
                return 'Please set the payment type to "doesn\'t need to" instead of setting the badge price to 0.'

    try:
        amount_refunded = int(float(attendee.amount_refunded))
        if amount_refunded < 0:
            return 'Amount Refunded must be positive'
        elif amount_refunded > amount_paid:
            return 'Amount Refunded cannot be greater than Amount Paid'
        elif attendee.paid == c.REFUNDED and amount_refunded == 0:
            return 'Amount Refunded may not be 0 if the attendee is marked Paid and Refunded'
    except:
        return "What you entered for Amount Refunded ({}) wasn't even a number".format(attendee.amount_refunded)

@validation.Attendee
def dealer_needs_group(attendee):
    if attendee.is_dealer and not attendee.badge_type == c.PSEUDO_DEALER_BADGE and not attendee.group_id:
        return 'Dealers must be associated with a group'

@validation.Attendee
def dupe_badge_num(attendee):
    if attendee.badge_num != 0 and attendee.session.query(Attendee)\
            .filter(Attendee.id != attendee.id)\
            .filter_by(badge_type=attendee.badge_type, badge_num=attendee.badge_num).count():
        return 'Another attendee already exists with that badge number!'

@validation.Attendee
def invalid_badge_num(attendee):
    try:
        badge_num = int(attendee.badge_num)
    except:
        return '{!r} is not a valid badge number'.format(attendee.badge_num)
    else:
        if attendee.badge_num != 0:
            min_num, max_num = c.BADGE_RANGES[attendee.badge_type]
            if not (min_num <= badge_num <= max_num):
                return '{} badge numbers must fall within {} and {}'.format(attendee.badge_type_label, min_num, max_num)

@validation.MPointsForCash
@validation.OldMPointExchange
def money_amount(model):
    if not str(model.amount).isdigit():
        return 'Amount must be a positive number'

Job.required = [('name', 'Job Name')]

@validation.Job
def slots(job):
    if job.slots < len(job.shifts):
        return 'You cannot reduce the number of slots to below the number of staffers currently signed up for this job'

@validation.Job
def time_conflicts(job):
    if not job.is_new:
        original_hours = Job(start_time=job.orig_value_of('start_time'), duration=job.orig_value_of('duration')).hours
        for shift in job.shifts:
            if job.hours.intersection(shift.attendee.hours - original_hours):
                return 'You cannot change this job to this time, because {} is already working a shift then'.format(shift.attendee.full_name)

@validation.OldMPointExchange
def oldmpointexchange_numbers(mpe):
    if not str(mpe.amount).isdigit():
        return 'MPoints must be a positive integer'

Sale.required = [('what', "What's being sold")]

@validation.Sale
def cash_and_mpoints(sale):
    if not str(sale.cash).isdigit() or int(sale.cash) < 0:
        return 'Cash must be a positive integer'
    if not str(sale.mpoints).isdigit() or int(sale.mpoints) < 0:
        return 'MPoints must be a positive integer'
<commit_msg>Only run badge_num checks if c.NUMBERED_BADGES Although theoretically events without numbered badges will never have numbered badges, data gets munged and this is not reliable. So we now only run checks on badge numbers if the event should care.<commit_after>"""
When an admin submits a form to create/edit an attendee/group/job/etc we usually want to perform some basic validations
on the data that was entered.  We put those validations here.  To make a validation for the Attendee model, you can
just write a function decorated with the @validation.Attendee decorator.  That function should return None on success
and an error string on failure.

In addition, you can define a set of required fields by setting the .required field like the AdminAccount.required list
below.  This should be a list of tuples where the first tuple element is the name of the field, and the second is the
name that should be displayed in the "XXX is a required field" error message.

To perform these validations, call the "check" method on the instance you're validating.  That method returns None
on success and a string error message on validation failure.
"""
from uber.common import *

AdminAccount.required = [('attendee', 'Attendee'), ('hashed', 'Password')]

@validation.AdminAccount
def duplicate_admin(account):
    if account.is_new:
        with Session() as session:
            if session.query(AdminAccount).filter_by(attendee_id=account.attendee_id).all():
                return 'That attendee already has an admin account'

@validation.AdminAccount
def has_email_address(account):
    if account.is_new:
        with Session() as session:
            if session.query(Attendee).filter_by(id=account.attendee_id).first().email == '':
                return "Attendee doesn't have a valid email set"

Group.required = [('name', 'Group Name')]

@prereg_validation.Group
def dealer_wares(group):
    if group.tables and not group.wares:
        return "You must provide a detailed explanation of what you sell for us to evaluate your submission"

@prereg_validation.Group
def dealer_website(group):
    if group.tables and not group.website:
        return "Please enter your business' website address"

@prereg_validation.Group
def dealer_description(group):
    if group.tables and not group.description:
        return "Please provide a brief description of your business"

@prereg_validation.Group
def dealer_address(group):
    if group.tables and not group.address and not c.COLLECT_FULL_ADDRESS:
        "Please provide your full address for tax purposes"

@validation.Group
def group_paid(group):
    try:
        amount = int(float(group.amount_paid))
        if amount < 0:
            return 'Amount Paid must be a number that is 0 or higher.'
    except:
        return "What you entered for Amount Paid ({}) isn't even a number".format(group.amount_paid)

def _invalid_phone_number(s):
    if not s.startswith('+'):
        return len(re.findall(r'\d', s)) != 10 or re.search(c.SAME_NUMBER_REPEATED, re.sub(r'[^0-9]', '', s))

def _invalid_zip_code(s):
    return len(re.findall(r'\d', s)) not in [5, 9]

def ignore_unassigned_and_placeholders(func):
    @wraps(func)
    def with_skipping(attendee):
        unassigned_group_reg = attendee.group_id and not attendee.first_name and not attendee.last_name
        valid_placeholder = attendee.placeholder and attendee.first_name and attendee.last_name
        if not unassigned_group_reg and not valid_placeholder:
            return func(attendee)
    return with_skipping

@prereg_validation.Attendee
def dealer_cellphone(attendee):
    if attendee.badge_type == c.PSEUDO_DEALER_BADGE and not attendee.cellphone:
        return 'Your phone number is required'

@prereg_validation.Attendee
def shirt_size(attendee):
    if attendee.amount_extra >= c.SHIRT_LEVEL and attendee.shirt == c.NO_SHIRT:
        return 'Your shirt size is required'

@prereg_validation.Attendee
def total_cost_over_paid(attendee):
    if attendee.total_cost < attendee.amount_paid:
        return 'You have already paid ${}, you cannot reduce your extras below that.'.format(attendee.amount_paid)

@validation.Attendee
@ignore_unassigned_and_placeholders
def full_name(attendee):
    if not attendee.first_name:
        return 'First Name is a required field'
    elif not attendee.last_name:
        return 'Last Name is a required field'

@validation.Attendee
@ignore_unassigned_and_placeholders
def age(attendee):
    if c.COLLECT_EXACT_BIRTHDATE:
        if not attendee.birthdate:
            return 'Please enter a date of birth.'
        elif attendee.birthdate > date.today():
            return 'You cannot be born in the future.'
    elif not attendee.age_group:
        return 'Please enter your age group'

@validation.Attendee
@ignore_unassigned_and_placeholders
def address(attendee):
    if c.COLLECT_FULL_ADDRESS:
        if not attendee.address1:
            return 'Please enter a street address.'
        if not attendee.city:
            return 'Please enter a city.'
        if not attendee.region:
            return 'Please enter a state, province, or region.'
        if not attendee.country:
            return 'Please enter a country.'

@validation.Attendee
@ignore_unassigned_and_placeholders
def email(attendee):
    if len(attendee.email) > 255:
        return 'Email addresses cannot be longer than 255 characters.'

    if (c.AT_OR_POST_CON and attendee.email and not re.match(c.EMAIL_RE, attendee.email)) or (not c.AT_OR_POST_CON and not re.match(c.EMAIL_RE, attendee.email)):
        return 'Enter a valid email address'

@validation.Attendee
@ignore_unassigned_and_placeholders
def emergency_contact(attendee):
    if not attendee.international and _invalid_phone_number(attendee.ec_phone):
        if c.COLLECT_FULL_ADDRESS:
            return 'Enter a 10-digit US phone number or include a country code (e.g. +44).'
        else:
            return 'Enter a 10-digit emergency contact number'

@validation.Attendee
@ignore_unassigned_and_placeholders
def cellphone(attendee):
    if attendee.cellphone and _invalid_phone_number(attendee.cellphone):
        if c.COLLECT_FULL_ADDRESS:
            return 'Enter a 10-digit US phone number or include a country code (e.g. +44).'
        else:
            return 'Your cellphone number was not a valid 10-digit phone number'

    if not attendee.no_cellphone and attendee.staffing and not attendee.cellphone:
        return "Cellphone number is required for volunteers (unless you don't own a cellphone)"

@validation.Attendee
@ignore_unassigned_and_placeholders
def zip_code(attendee):
    if not attendee.international and not c.AT_OR_POST_CON:
        if _invalid_zip_code(attendee.zip_code):
            return 'Enter a valid zip code'

@validation.Attendee
def allowed_to_volunteer(attendee):
    if attendee.staffing and not attendee.age_group_conf['can_volunteer'] and attendee.badge_type != c.STAFF_BADGE and c.PRE_CON:
        return 'Volunteers cannot be ' + attendee.age_group_conf['desc']

@validation.Attendee
def allowed_to_register(attendee):
    if not attendee.age_group_conf['can_register']:
        return 'Attendees ' + attendee.age_group_conf['desc'] + ' years of age do not need to register, but MUST be accompanied by a parent at all times!'

@validation.Attendee
def printed_badge_deadline(attendee):
    if attendee.is_new and attendee.has_personalized_badge and not c.SHIFT_CUSTOM_BADGES:
        return 'Custom badges have already been ordered so you cannot create new {} badges'.format(attendee.badge_type_label)

@validation.Attendee
def group_leadership(attendee):
    if attendee.session and not attendee.group_id:
        orig_group_id = attendee.orig_value_of('group_id')
        if orig_group_id and attendee.id == attendee.session.group(orig_group_id).leader_id:
            return 'You cannot remove the leader of a group from that group; make someone else the leader first'

@validation.Attendee
def banned_volunteer(attendee):
    if (attendee.ribbon == c.VOLUNTEER_RIBBON or attendee.staffing) and attendee.full_name in c.BANNED_STAFFERS:
        return "We've declined to invite {} back as a volunteer, ".format(attendee.full_name) + (
                    'talk to Stops to override if necessary' if c.AT_THE_CON else
                    'Please contact us via {} if you believe this is in error'.format(c.CONTACT_URL))

@validation.Attendee
def attendee_money(attendee):
    try:
        amount_paid = int(float(attendee.amount_paid))
        if amount_paid < 0:
            return 'Amount Paid cannot be less than zero'
    except:
        return "What you entered for Amount Paid ({}) wasn't even a number".format(attendee.amount_paid)

    try:
        amount_extra = int(float(attendee.amount_extra or 0))
        if amount_extra < 0:
            return 'Amount extra must be a positive integer'
    except:
        return 'Invalid amount extra ({})'.format(attendee.amount_extra)

    if attendee.overridden_price is not None:
        try:
            overridden_price = int(float(attendee.overridden_price))
            if overridden_price < 0:
                return 'Overridden price must be a positive integer'
        except:
            return 'Invalid overridden price ({})'.format(attendee.overridden_price)
        else:
            if attendee.overridden_price == 0:
                return 'Please set the payment type to "doesn\'t need to" instead of setting the badge price to 0.'

    try:
        amount_refunded = int(float(attendee.amount_refunded))
        if amount_refunded < 0:
            return 'Amount Refunded must be positive'
        elif amount_refunded > amount_paid:
            return 'Amount Refunded cannot be greater than Amount Paid'
        elif attendee.paid == c.REFUNDED and amount_refunded == 0:
            return 'Amount Refunded may not be 0 if the attendee is marked Paid and Refunded'
    except:
        return "What you entered for Amount Refunded ({}) wasn't even a number".format(attendee.amount_refunded)

@validation.Attendee
def dealer_needs_group(attendee):
    if attendee.is_dealer and not attendee.badge_type == c.PSEUDO_DEALER_BADGE and not attendee.group_id:
        return 'Dealers must be associated with a group'

@validation.Attendee
def dupe_badge_num(attendee):
    if c.NUMBERED_BADGES and attendee.badge_num != 0 and attendee.session.query(Attendee)\
            .filter(Attendee.id != attendee.id)\
            .filter_by(badge_type=attendee.badge_type, badge_num=attendee.badge_num).count():
        return 'Another attendee already exists with that badge number!'

@validation.Attendee
def invalid_badge_num(attendee):
    if c.NUMBERED_BADGES:
        try:
            badge_num = int(attendee.badge_num)
        except:
            return '{!r} is not a valid badge number'.format(attendee.badge_num)
        else:
            if attendee.badge_num != 0:
                min_num, max_num = c.BADGE_RANGES[attendee.badge_type]
                if not (min_num <= badge_num <= max_num):
                    return '{} badge numbers must fall within {} and {}'.format(attendee.badge_type_label, min_num, max_num)

@validation.MPointsForCash
@validation.OldMPointExchange
def money_amount(model):
    if not str(model.amount).isdigit():
        return 'Amount must be a positive number'

Job.required = [('name', 'Job Name')]

@validation.Job
def slots(job):
    if job.slots < len(job.shifts):
        return 'You cannot reduce the number of slots to below the number of staffers currently signed up for this job'

@validation.Job
def time_conflicts(job):
    if not job.is_new:
        original_hours = Job(start_time=job.orig_value_of('start_time'), duration=job.orig_value_of('duration')).hours
        for shift in job.shifts:
            if job.hours.intersection(shift.attendee.hours - original_hours):
                return 'You cannot change this job to this time, because {} is already working a shift then'.format(shift.attendee.full_name)

@validation.OldMPointExchange
def oldmpointexchange_numbers(mpe):
    if not str(mpe.amount).isdigit():
        return 'MPoints must be a positive integer'

Sale.required = [('what', "What's being sold")]

@validation.Sale
def cash_and_mpoints(sale):
    if not str(sale.cash).isdigit() or int(sale.cash) < 0:
        return 'Cash must be a positive integer'
    if not str(sale.mpoints).isdigit() or int(sale.mpoints) < 0:
        return 'MPoints must be a positive integer'
<|endoftext|>


<commit_before>from .base import *
from .secret import EMAIL_HOST_PASSWORD, ACCOUNT_NUMBER, SECRET_KEY

ALLOWED_HOSTS = ['localhost', '127.0.0.1']
DEBUG = False

INSTALLED_APPS += (
    'debug_toolbar',
    'django_nose',
    'django_webtest',
    'django_extensions',
)

TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'

MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware', )
INTERNAL_IPS = ("127.0.0.1", "localhost")

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

<commit_msg>setting debug back to True for dev<commit_after>from .base import *
from .secret import EMAIL_HOST_PASSWORD, ACCOUNT_NUMBER, SECRET_KEY

ALLOWED_HOSTS = ['localhost', '127.0.0.1']
DEBUG = True

INSTALLED_APPS += (
    'debug_toolbar',
    'django_nose',
    'django_webtest',
    'django_extensions',
)

TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'

MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware', )
INTERNAL_IPS = ("127.0.0.1", "localhost")

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

import re
import copy

from parser_exception import ParserException
import utils
import xml_utils

class BaseType(object):
	""" Abstract base class for all types

	Attributes:
	name		--	Type name. Must be unique.
	reference	--	True if the type is only used as reference for other
					types and should not be included in the output.
	description	--	Description of the type
	string		--  Short description for enum members
	isEnum		--	True if the type is an enum type
	isStruct	--	analog
	isTypedef	--	analog
	isBuiltIn	--	analog
	level		--	hierarchy level. Built-in types have level -1, the level
					of a type is the highest level of a subtype plus one.
	size		--	Size
	"""
	def __init__(self, node):
		self.node = node

		self.name = node.get('name')
		self._check_name()
		self.description = None
		self.string = None

		self.isEnum = False
		self.isStruct = False
		self.isTypedef = False
		self.isBuiltIn = False

		self.level = None
		self.size = None

	def _check_name(self):
		utils.check_name(self.name)

	def evaluate(self, tree):
		""" Load the state of the class from the corresponding XML node

		Keyword arguments:
		tree	--	current tree of the communcation structure. Must contain
					every available type, but some types may not be fully
					evaluated
		"""
		pass

	def create_hierarchy(self):
		"""	Create the type hierarchy

		This method calculates the values for self.size and self.level. Must
		not be called before all types are fully created.
		"""
		pass

	def flattened(self):
		""" Access the version with the flattened hierarchy """
		return self

	def __cmp__(self, other):
		""" Compare two types

		If types are sorted they are sorted first by level and then by name.
		"""
		if isinstance(other, BaseType):
			return cmp(self.level, other.level) or cmp(self.name, other.name)
		else:
			return 1

class BuiltIn(BaseType):
	""" Built-in types

	These types correspond directly to types available by default in the
	target environment. The hierarchy level will always be -1 (lowest level).
	"""
	def __init__(self, node):
		BaseType.__init__(self, node)

		self.isBuiltIn = True
		self.level = -1

	def evaluate(self, tree):
		if self.node is None:
			return

		self.description = xml_utils.get_description(self.node)
		self.string = xml_utils.get_string(self.node)
		self.size = int(self.node.get('size'))

		self.node = None

	def _check_name(self):
		""" Built-in types need no check """
		pass

	def dump(self):
		return "%s : built-in|%i [%i]" % (self.name, self.level, self.size)

	def __str__(self):
		return "%s : built-in" % self.name

class Enum(BaseType):

	class Element(object):
		""" Sub-element of an enum-type """
		def __init__(self, node):
			""" Constructor

			The name of the element has to be all upper case with underscores.
			"""
			self.name = node.get('name')
			if not re.match("^[0-9A-Z_]*$", self.name):
				raise ParserException("Attribute name of element in enum has to be UPPER_UNDERSCORE_STYLE (found: '%s')" % (self.name))

			self.description = xml_utils.get_description(node)
			self.string = xml_utils.get_string(node)

			value = node.get('value')
			self.value = None if (value is None) else int(value, 0)

		def __str__(self):
			return "%s = %s" % (self.name, self.value)

	def __init__(self, node):
		BaseType.__init__(self, node)

		self._last_value = 0
		self.elements = []

		self.isEnum = True

		# an enum don't depend on other types
		self.level = 0
		self.size = 1		# FIXME calculate actual size depending on the value for the enum elements

	def iter(self):
		""" Iterate over all sub-elements of the enum """
		for element in self.elements:
			yield element

	def evaluate(self, tree):
		if self.node is None:
			return

		self.description = xml_utils.get_description(self.node)
		self.string = xml_utils.get_string(self.node)
		for node in self.node.findall('element'):
			self.__add(self.Element(node))

		self.node = None

	def __add(self, element):
		""" Add an element to the enum.

		This has to be done in the order the elements should apear later.
		"""
		if element.value == None:
			element.value = self._last_value
			self._last_value += 1
		else:
			try:
				self._last_value = element.value + 1
			except ValueError:
				pass

		self.elements.append(element)

	def create_hierarchy(self):
		pass

	def dump(self):
		str = "%s : enum|%i [%i]\n" % (self.name, self.level, self.size)
		for element in self.iter():
			str += "  + %s\n" % element
		return str[:-1]

	def __str__(self):
		return "%s : enum|%i" % (self.name, self.level)

class SubType:
	""" Subordinate type for struct and typedefs.

	Used to distinguish between normal and array types for the subtype of
	struct elements and typedefs.

	Attributes:
	raw		--	raw typename.
	name	--	type without a possible array definition.
	count	--	Number of array elements. Defaults to 1 if the type is no array.
	isArray	--	Subtype is an array

	Example:
	type = SubType("uint8_t")
	=> .raw = "uint8_t"
	   .name = "uint8_t"
	   .isArray = False
	   .count = 1
	   .type = BuiltIn("uint8_t")

	type = SubType("char[8])
	=> .raw = "char[8]"
	   .name = "char"
	   .isArray = True
	   .count = 8
	   .type = BuiltIn("char")

	"""
	def __init__(self, value, types):
		""" Constructor

		Keyword Arguments:
		value	-- type name
		types	-- list of all available types
		"""
		self.raw = value
		if value.endswith(']'):
			self.isArray = True
			self.name, number = value.split('[')
			self.count = number[:-1]
		else:
			self.isArray = False
			self.count = 1
			self.name = value

		try:
			self.type = types[self.name]
		except KeyError:
			raise ParserException("Unknown type '%s'" % self.name)
		self.size = None

	def __str__(self):
		return self.raw

class Struct(BaseType):
	""" Representation of a Struct

	Attributes:
	size	--	Size of the struct in bytes. Will always return the total size
				of the flattened struct if the struct is part of inheritence
				structure.
	"""
	class Element(object):

		def __init__(self, node, tree):
			self.name = node.get('name')

			self.description = xml_utils.get_description(node)
			self.string = xml_utils.get_string(node)
			self.subtype = SubType(node.get('type'), tree.types)
			self.unit = node.get('unit')
			self.value = node.get('value')

			self.level = None
			self.size = None

		def create_hierarchy(self):
			subtype = self.subtype.type
			if subtype.level is None:
				subtype.create_hierarchy()

			self.level = subtype.level
			self.size = subtype.size

		def __str__(self):
			return "%s : %s" % (self.name, self.subtype)

	def __init__(self, node):
		BaseType.__init__(self, node)

		self.isStruct = True
		self.elements = []
		self.extends = None
		self.extending = []
		self.typeIdentifier = None

		self.__flattened = None
		self.__typeIdentifierName = None

	def iter(self):
		""" Iterate over all sub-elements of the enum """
		for element in self.elements:
			yield element

	def evaluate(self, tree):
		if self.node is None:
			return

		self.description = xml_utils.get_description(self.node)
		self.string = xml_utils.get_string(self.node)
		for node in self.node.findall('element'):
			try:
				self.elements.append(self.Element(node, tree))
			except ParserException as e:
				raise ParserException("Error in definition of struct '%s': %s!" % (self.name, e))

		basetype = self.node.get('extends')
		if basetype is not None:
			try:
				self.extends = tree.types[basetype]
				if not self.extends.isStruct:
					raise ParserException("Struct '%s' is derived from non struct '%s'!" % (self.name, self.extends.name))
				if self.extends.extends:
					raise ParserException("Struct '%s' extends struct '%s'. Structs are only allowed to extend from those Structs, which do not extend anything!" % (self.name, self.extends.name))
				self.__typeIdentifierName = self.node.get('typeIdentifier')
				if self.__typeIdentifierName is None:
					raise ParserException("Struct '%s' does extend '%s', but does not provide the attribute 'typeIdentifier'!" % (self.name, self.extends.name))

			except KeyError:
				raise ParserException("Unknown super type '%s' in struct '%s'!" % (basetype, self.name))
		self.node = None

	def create_hierarchy(self):
		""" Create hierarchy

		For this method self.size = 0 is used as sepecial value to detect
		loops in the definition of types. In normal operation size will never be
		zero, only during hierarchy creation.
		"""
		if self.level is not None:
			return

		self.size = 0
		size = 0
		self.level = 0
		for element in self.elements:
			if element.size == 0:
				raise ParserException("Loop in the definition of '%s' and '%s' detected!" % (self.name, self.element.name))
			element.create_hierarchy()

			size += element.size
			self.level = max(self.level, element.level)

		if self.extends is not None:
			if self.extends.size == 0:
				raise ParserException("Loop in the definition of '%s' and '%s' detected!" % (self.name, self.extends.name))
			self.extends.create_hierarchy()
			typeIdentifierStructElement = self.extends.elements[0]
			if not typeIdentifierStructElement.subtype.type.isEnum:
				raise ParserException("Struct '%s' is extended by Struct '%s'. "  \
					"Structs which are extended by other must have an element named " \
					"'type' of any enum type as their first element! It is used for " \
					"type distinguishing at runtime." \
						% (self.extends.name, self.name))
			if not typeIdentifierStructElement.name == 'type':
				raise ParserException("Struct '%s' is extended by Struct '%s'. Structs" \
					"which are extended by other must have an element named 'type' as" \
					"their first element! It is used for type distinguishing at runtime." \
						% (self.extends.name, self.name))

			for enumElement in typeIdentifierStructElement.subtype.type.elements:
				if enumElement.name == self.__typeIdentifierName:
					self.typeIdentifier = enumElement
					break
			if not self.typeIdentifier:
				raise ParserException("Struct '%s' extends Struct '%s', but it's " \
					"typeIdentifier '%s' is not member of enum '%s' which is the " \
					"type of '%s.type'."
						% (self.name, self.extends.name, self.__typeIdentifierName,
							typeIdentifierStructElement.subtype.type.name, self.extends.name))

			self.extends.__addExtending(self)
			size += self.extends.size
			self.level = max(self.level, self.extends.level)

		if size > 48:
			raise ParserException("Struct '%s' is with %i Byte too big. The maximum " \
				"packet size is 48 Byte!" % (self.name, size))

		self.size = size
		self.level += 1

	def flattened(self):
		if self.__flattened is None:
			if self.extends is None:
				self.__flattened = self
			else:
				self.__flattened = copy.copy(self)
				self.__flattened.elements = self.elements[:]

				# prepend all elements for the super type
				self.__flattened.elements[0:0] = self.extends.flattened().elements

		return self.__flattened

	def dump(self):
		str = "%s : struct|%i [%i]\n" % \
			(self.name, self.level, self.size)
		for element in self.iter():
			str += "  + %s\n" % element
		return str[:-1]

	def __str__(self):
		return "%s : struct|%i" % (self.name, self.level)

	def __addExtending(self, extending):
		for struct in self.extending:
			if struct.typeIdentifier == extending.typeIdentifier:
				raise ParserException("Duplicate TypeIdentifier '%s' in Struct group extending '%s'. ('%s' and '%s')" % (extending.typeIdentifier.name, self.name, struct.name, extending.name))
		self.extending.append(extending)

class Typedef(BaseType):

	def __init__(self, node):
		BaseType.__init__(self, node)

		self.subtype = None
		self.unit = None

		self.isTypedef = True

	def evaluate(self, tree):
		self.description = xml_utils.get_description(self.node)
		self.string = xml_utils.get_string(self.node)
		self.unit = self.node.get('unit')
		try:
			self.subtype = SubType(self.node.get('type'), tree.types)
		except ParserException as e:
			raise ParserException("Error in definition of typedef '%s': %s" % (self.name, e))

	def create_hierarchy(self):
		""" Create hierarchy

		See Struct.create_hierarchy() for a detailed description of this
		method.
		"""
		if self.level is not None:
			return

		self.size = 0
		subtype = self.subtype.type
		if subtype.level is None:
			if self.subtype.size == 0:
				raise ParserException("Loop in the definition of '%s' and '%s' detected!" % (self.name, self.subtype.name))
			subtype.create_hierarchy()

		self.level = subtype.level + 1
		self.size = subtype.size

	def dump(self):
		return "%s : typedef|%i [%i]\n  -> %s" % (self.name, self.level, self.size, self.subtype)

	def __str__(self):
		return "%s : typedef|%i" % (self.name, self.level)
<commit_msg>Using the string parameter on ENUMs for print.<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

import re
import copy

from parser_exception import ParserException
import utils
import xml_utils

class BaseType(object):
	""" Abstract base class for all types

	Attributes:
	name		--	Type name. Must be unique.
	reference	--	True if the type is only used as reference for other
					types and should not be included in the output.
	description	--	Description of the type
	string		--  Short description for enum members
	isEnum		--	True if the type is an enum type
	isStruct	--	analog
	isTypedef	--	analog
	isBuiltIn	--	analog
	level		--	hierarchy level. Built-in types have level -1, the level
					of a type is the highest level of a subtype plus one.
	size		--	Size
	"""
	def __init__(self, node):
		self.node = node

		self.name = node.get('name')
		self._check_name()
		self.description = None
		self.string = None

		self.isEnum = False
		self.isStruct = False
		self.isTypedef = False
		self.isBuiltIn = False

		self.level = None
		self.size = None

	def _check_name(self):
		utils.check_name(self.name)

	def evaluate(self, tree):
		""" Load the state of the class from the corresponding XML node

		Keyword arguments:
		tree	--	current tree of the communcation structure. Must contain
					every available type, but some types may not be fully
					evaluated
		"""
		pass

	def create_hierarchy(self):
		"""	Create the type hierarchy

		This method calculates the values for self.size and self.level. Must
		not be called before all types are fully created.
		"""
		pass

	def flattened(self):
		""" Access the version with the flattened hierarchy """
		return self

	def __cmp__(self, other):
		""" Compare two types

		If types are sorted they are sorted first by level and then by name.
		"""
		if isinstance(other, BaseType):
			return cmp(self.level, other.level) or cmp(self.name, other.name)
		else:
			return 1

class BuiltIn(BaseType):
	""" Built-in types

	These types correspond directly to types available by default in the
	target environment. The hierarchy level will always be -1 (lowest level).
	"""
	def __init__(self, node):
		BaseType.__init__(self, node)

		self.isBuiltIn = True
		self.level = -1

	def evaluate(self, tree):
		if self.node is None:
			return

		self.description = xml_utils.get_description(self.node)
		self.string = xml_utils.get_string(self.node)
		self.size = int(self.node.get('size'))

		self.node = None

	def _check_name(self):
		""" Built-in types need no check """
		pass

	def dump(self):
		return "%s : built-in|%i [%i]" % (self.name, self.level, self.size)

	def __str__(self):
		return "%s : built-in" % self.name

class Enum(BaseType):

	class Element(object):
		""" Sub-element of an enum-type """
		def __init__(self, node):
			""" Constructor

			The name of the element has to be all upper case with underscores.
			"""
			self.name = node.get('name')
			if not re.match("^[0-9A-Z_]*$", self.name):
				raise ParserException("Attribute name of element in enum has to be UPPER_UNDERSCORE_STYLE (found: '%s')" % (self.name))

			self.string = node.get('string')
			if self.string is None:
				self.string = self.name

			self.description = xml_utils.get_description(node)
			self.string = xml_utils.get_string(node)

			value = node.get('value')
			self.value = None if (value is None) else int(value, 0)

		def __str__(self):
			return "%s = %s" % (self.name, self.value)

	def __init__(self, node):
		BaseType.__init__(self, node)

		self._last_value = 0
		self.elements = []

		self.isEnum = True

		# an enum don't depend on other types
		self.level = 0
		self.size = 1		# FIXME calculate actual size depending on the value for the enum elements

	def iter(self):
		""" Iterate over all sub-elements of the enum """
		for element in self.elements:
			yield element

	def evaluate(self, tree):
		if self.node is None:
			return

		self.description = xml_utils.get_description(self.node)
		self.string = xml_utils.get_string(self.node)
		for node in self.node.findall('element'):
			self.__add(self.Element(node))

		self.node = None

	def __add(self, element):
		""" Add an element to the enum.

		This has to be done in the order the elements should apear later.
		"""
		if element.value == None:
			element.value = self._last_value
			self._last_value += 1
		else:
			try:
				self._last_value = element.value + 1
			except ValueError:
				pass

		self.elements.append(element)

	def create_hierarchy(self):
		pass

	def dump(self):
		str = "%s : enum|%i [%i]\n" % (self.name, self.level, self.size)
		for element in self.iter():
			str += "  + %s\n" % element
		return str[:-1]

	def __str__(self):
		return "%s : enum|%i" % (self.name, self.level)

class SubType:
	""" Subordinate type for struct and typedefs.

	Used to distinguish between normal and array types for the subtype of
	struct elements and typedefs.

	Attributes:
	raw		--	raw typename.
	name	--	type without a possible array definition.
	count	--	Number of array elements. Defaults to 1 if the type is no array.
	isArray	--	Subtype is an array

	Example:
	type = SubType("uint8_t")
	=> .raw = "uint8_t"
	   .name = "uint8_t"
	   .isArray = False
	   .count = 1
	   .type = BuiltIn("uint8_t")

	type = SubType("char[8])
	=> .raw = "char[8]"
	   .name = "char"
	   .isArray = True
	   .count = 8
	   .type = BuiltIn("char")

	"""
	def __init__(self, value, types):
		""" Constructor

		Keyword Arguments:
		value	-- type name
		types	-- list of all available types
		"""
		self.raw = value
		if value.endswith(']'):
			self.isArray = True
			self.name, number = value.split('[')
			self.count = number[:-1]
		else:
			self.isArray = False
			self.count = 1
			self.name = value

		try:
			self.type = types[self.name]
		except KeyError:
			raise ParserException("Unknown type '%s'" % self.name)
		self.size = None

	def __str__(self):
		return self.raw

class Struct(BaseType):
	""" Representation of a Struct

	Attributes:
	size	--	Size of the struct in bytes. Will always return the total size
				of the flattened struct if the struct is part of inheritence
				structure.
	"""
	class Element(object):

		def __init__(self, node, tree):
			self.name = node.get('name')

			self.description = xml_utils.get_description(node)
			self.string = xml_utils.get_string(node)
			self.subtype = SubType(node.get('type'), tree.types)
			self.unit = node.get('unit')
			self.value = node.get('value')

			self.level = None
			self.size = None

		def create_hierarchy(self):
			subtype = self.subtype.type
			if subtype.level is None:
				subtype.create_hierarchy()

			self.level = subtype.level
			self.size = subtype.size

		def __str__(self):
			return "%s : %s" % (self.name, self.subtype)

	def __init__(self, node):
		BaseType.__init__(self, node)

		self.isStruct = True
		self.elements = []
		self.extends = None
		self.extending = []
		self.typeIdentifier = None

		self.__flattened = None
		self.__typeIdentifierName = None

	def iter(self):
		""" Iterate over all sub-elements of the enum """
		for element in self.elements:
			yield element

	def evaluate(self, tree):
		if self.node is None:
			return

		self.description = xml_utils.get_description(self.node)
		self.string = xml_utils.get_string(self.node)
		for node in self.node.findall('element'):
			try:
				self.elements.append(self.Element(node, tree))
			except ParserException as e:
				raise ParserException("Error in definition of struct '%s': %s!" % (self.name, e))

		basetype = self.node.get('extends')
		if basetype is not None:
			try:
				self.extends = tree.types[basetype]
				if not self.extends.isStruct:
					raise ParserException("Struct '%s' is derived from non struct '%s'!" % (self.name, self.extends.name))
				if self.extends.extends:
					raise ParserException("Struct '%s' extends struct '%s'. Structs are only allowed to extend from those Structs, which do not extend anything!" % (self.name, self.extends.name))
				self.__typeIdentifierName = self.node.get('typeIdentifier')
				if self.__typeIdentifierName is None:
					raise ParserException("Struct '%s' does extend '%s', but does not provide the attribute 'typeIdentifier'!" % (self.name, self.extends.name))

			except KeyError:
				raise ParserException("Unknown super type '%s' in struct '%s'!" % (basetype, self.name))
		self.node = None

	def create_hierarchy(self):
		""" Create hierarchy

		For this method self.size = 0 is used as sepecial value to detect
		loops in the definition of types. In normal operation size will never be
		zero, only during hierarchy creation.
		"""
		if self.level is not None:
			return

		self.size = 0
		size = 0
		self.level = 0
		for element in self.elements:
			if element.size == 0:
				raise ParserException("Loop in the definition of '%s' and '%s' detected!" % (self.name, self.element.name))
			element.create_hierarchy()

			size += element.size
			self.level = max(self.level, element.level)

		if self.extends is not None:
			if self.extends.size == 0:
				raise ParserException("Loop in the definition of '%s' and '%s' detected!" % (self.name, self.extends.name))
			self.extends.create_hierarchy()
			typeIdentifierStructElement = self.extends.elements[0]
			if not typeIdentifierStructElement.subtype.type.isEnum:
				raise ParserException("Struct '%s' is extended by Struct '%s'. "  \
					"Structs which are extended by other must have an element named " \
					"'type' of any enum type as their first element! It is used for " \
					"type distinguishing at runtime." \
						% (self.extends.name, self.name))
			if not typeIdentifierStructElement.name == 'type':
				raise ParserException("Struct '%s' is extended by Struct '%s'. Structs" \
					"which are extended by other must have an element named 'type' as" \
					"their first element! It is used for type distinguishing at runtime." \
						% (self.extends.name, self.name))

			for enumElement in typeIdentifierStructElement.subtype.type.elements:
				if enumElement.name == self.__typeIdentifierName:
					self.typeIdentifier = enumElement
					break
			if not self.typeIdentifier:
				raise ParserException("Struct '%s' extends Struct '%s', but it's " \
					"typeIdentifier '%s' is not member of enum '%s' which is the " \
					"type of '%s.type'."
						% (self.name, self.extends.name, self.__typeIdentifierName,
							typeIdentifierStructElement.subtype.type.name, self.extends.name))

			self.extends.__addExtending(self)
			size += self.extends.size
			self.level = max(self.level, self.extends.level)

		if size > 48:
			raise ParserException("Struct '%s' is with %i Byte too big. The maximum " \
				"packet size is 48 Byte!" % (self.name, size))

		self.size = size
		self.level += 1

	def flattened(self):
		if self.__flattened is None:
			if self.extends is None:
				self.__flattened = self
			else:
				self.__flattened = copy.copy(self)
				self.__flattened.elements = self.elements[:]

				# prepend all elements for the super type
				self.__flattened.elements[0:0] = self.extends.flattened().elements

		return self.__flattened

	def dump(self):
		str = "%s : struct|%i [%i]\n" % \
			(self.name, self.level, self.size)
		for element in self.iter():
			str += "  + %s\n" % element
		return str[:-1]

	def __str__(self):
		return "%s : struct|%i" % (self.name, self.level)

	def __addExtending(self, extending):
		for struct in self.extending:
			if struct.typeIdentifier == extending.typeIdentifier:
				raise ParserException("Duplicate TypeIdentifier '%s' in Struct group extending '%s'. ('%s' and '%s')" % (extending.typeIdentifier.name, self.name, struct.name, extending.name))
		self.extending.append(extending)

class Typedef(BaseType):

	def __init__(self, node):
		BaseType.__init__(self, node)

		self.subtype = None
		self.unit = None

		self.isTypedef = True

	def evaluate(self, tree):
		self.description = xml_utils.get_description(self.node)
		self.string = xml_utils.get_string(self.node)
		self.unit = self.node.get('unit')
		try:
			self.subtype = SubType(self.node.get('type'), tree.types)
		except ParserException as e:
			raise ParserException("Error in definition of typedef '%s': %s" % (self.name, e))

	def create_hierarchy(self):
		""" Create hierarchy

		See Struct.create_hierarchy() for a detailed description of this
		method.
		"""
		if self.level is not None:
			return

		self.size = 0
		subtype = self.subtype.type
		if subtype.level is None:
			if self.subtype.size == 0:
				raise ParserException("Loop in the definition of '%s' and '%s' detected!" % (self.name, self.subtype.name))
			subtype.create_hierarchy()

		self.level = subtype.level + 1
		self.size = subtype.size

	def dump(self):
		return "%s : typedef|%i [%i]\n  -> %s" % (self.name, self.level, self.size, self.subtype)

	def __str__(self):
		return "%s : typedef|%i" % (self.name, self.level)
<|endoftext|>


<commit_before>#! /usr/bin/python
#
# Copyright (c) 2011-2015, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import EddParser
from PFWScriptGenerator import PfwScriptTranslator
import hostConfig

import argparse
import re
import sys
import tempfile
import os
import logging
import subprocess

def parseArgs():
    argparser = argparse.ArgumentParser(description="Parameter-Framework XML \
        Settings file generator.\n\
        Exit with the number of (recoverable or not) error that occured.")
    argparser.add_argument('--toplevel-config',
            help="Top-level parameter-framework configuration file. Mandatory.",
            metavar="TOPLEVEL_CONFIG_FILE",
            required=True)
    argparser.add_argument('--criteria',
            help="Criteria file, in '<type> <name> : <value> <value...>' \
        format. Mandatory.",
            metavar="CRITERIA_FILE",
            type=argparse.FileType('r'),
            required=True)
    argparser.add_argument('--initial-settings',
            help="Initial XML settings file (containing a \
        <ConfigurableDomains>  tag",
            nargs='?',
            default=None,
            metavar="XML_SETTINGS_FILE")
    argparser.add_argument('--add-domains',
            help="List of single domain files (each containing a single \
        <ConfigurableDomain> tag",
            metavar="XML_DOMAIN_FILE",
            nargs='*',
            dest='xml_domain_files',
            default=[])
    argparser.add_argument('--add-edds',
            help="List of files in EDD syntax (aka \".pfw\" files)",
            metavar="EDD_FILE",
            type=argparse.FileType('r'),
            nargs='*',
            default=[],
            dest='edd_files')
    argparser.add_argument('--schemas-dir',
            help="Directory of parameter-framework XML Schemas for generation \
        validation",
            default=None)
    argparser.add_argument('--target-schemas-dir',
            help="Ignored. Kept for retro-compatibility")
    argparser.add_argument('--validate',
            help="Validate the settings against XML schemas",
            action='store_true')
    argparser.add_argument('--verbose',
            action='store_true')

    return argparser.parse_args()

def parseCriteria(criteriaFile):
    # Parse a criteria file
    #
    # This file define one criteria per line; they should respect this format:
    #
    # <type> <name> : <values>
    #
    # Where <type> is 'InclusiveCriterion' or 'ExclusiveCriterion';
    #       <name> is any string w/o whitespace
    #       <values> is a list of whitespace-separated values, each of which is any
    #                string w/o a whitespace
    criteria_pattern = re.compile(
        r"^(?P<type>(?:Inclusive|Exclusive)Criterion)\s*" \
        r"(?P<name>\S+)\s*:\s*" \
        r"(?P<values>.*)$")
    criterion_inclusiveness_table = {
        'InclusiveCriterion' : "inclusive",
        'ExclusiveCriterion' : "exclusive"}

    all_criteria = []
    for line_number, line in enumerate(criteriaFile):
        match = criteria_pattern.match(line)
        if not match:
            raise ValueError("The following line is invalid: {}:{}\n{}".format(
                criteriaFile.name, line_number, line))

        criterion_name = match.groupdict()['name']
        criterion_type = match.groupdict()['type']
        criterion_values = re.split("\s*", match.groupdict()['values'])

        criterion_inclusiveness = criterion_inclusiveness_table[criterion_type]

        all_criteria.append({
            "name" : criterion_name,
            "inclusive" : criterion_inclusiveness,
            "values" : criterion_values})

    return all_criteria

def parseEdd(EDDFiles, verbose):
    parsed_edds = []

    for edd_file in EDDFiles:
        try:
            root = EddParser.Parser().parse(edd_file, verbose)
        except EddParser.MySyntaxError as ex:
            logging.critical(str(ex))
            logging.info("EXIT ON FAILURE")
            exit(2)

        try:
            root.propagate()
        except EddParser.MyPropagationError, ex :
            logging.critical(str(ex))
            logging.info("EXIT ON FAILURE")
            exit(1)

        parsed_edds.append((edd_file.name, root))
    return parsed_edds

def generateDomainCommands(logging, all_criteria, initial_settings, xml_domain_files, parsed_edds):
        # create and inject all the criteria
        logging.info("Creating all criteria")
        for criterion in all_criteria:
            yield ["createSelectionCriterion", criterion['inclusive'],
                   criterion['name']] + criterion['values']

        yield ["start"]

        # Import initial settings file
        if initial_settings:
            logging.info("Importing initial settings file {}".format(initial_settings))
            yield ["importDomainsWithSettingsXML", initial_settings]

        # Import each standalone domain files
        for domain_file in xml_domain_files:
            logging.info("Importing single domain file {}".format(domain_file))
            yield ["importDomainWithSettingsXML", domain_file]

        # Generate the script for each EDD file
        for filename, parsed_edd in parsed_edds:
            logging.info("Translating and injecting EDD file {}".format(filename))
            translator = PfwScriptTranslator()
            parsed_edd.translate(translator)
            for command in translator.getScript():
                yield command

def main():
    logging.root.setLevel(logging.INFO)
    args = parseArgs()

    all_criteria = parseCriteria(args.criteria)

    #
    # EDD files (aka ".pfw" files)
    #
    parsed_edds = parseEdd(args.edd_files, args.verbose)

    # We need to modify the toplevel configuration file to account for differences
    # between development setup and target (installation) setup, in particular, the
    # TuningMwith ode must be enforced, regardless of what will be allowed on the target
    fake_toplevel_config = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".xml",
                                                       prefix="TMPdomainGeneratorPFConfig_")

    install_path = os.path.dirname(os.path.realpath(args.toplevel_config))
    hostConfig.configure(
            infile=args.toplevel_config,
            outfile=fake_toplevel_config,
            structPath=install_path)
    fake_toplevel_config.close()

    # Create the connector. Pipe its input to us in order to write commands;
    # connect its output to stdout in order to have it dump the domains
    # there; connect its error output to stderr.
    connector = subprocess.Popen(["domainGeneratorConnector",
                            fake_toplevel_config.name,
                            'verbose' if args.verbose else 'no-verbose',
                            'validate' if args.validate else 'no-validate',
                            args.schemas_dir],
                           stdout=sys.stdout, stdin=subprocess.PIPE, stderr=sys.stderr)

    initial_settings = None
    if args.initial_settings:
        initial_settings = os.path.realpath(args.initial_settings)

    for command in generateDomainCommands(logging, all_criteria, initial_settings,
                                       args.xml_domain_files, parsed_edds):
        connector.stdin.write('\0'.join(command))
        connector.stdin.write("\n")

    # Closing the connector's input triggers the domain generation
    connector.stdin.close()
    connector.wait()
    os.remove(fake_toplevel_config.name)
    return connector.returncode

# If this file is directly executed
if __name__ == "__main__":
    exit(main())
<commit_msg>Test: disable edd parsing logging<commit_after>#! /usr/bin/python
#
# Copyright (c) 2011-2015, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import EddParser
from PFWScriptGenerator import PfwScriptTranslator
import hostConfig

import argparse
import re
import sys
import tempfile
import os
import logging
import subprocess

def parseArgs():
    argparser = argparse.ArgumentParser(description="Parameter-Framework XML \
        Settings file generator.\n\
        Exit with the number of (recoverable or not) error that occured.")
    argparser.add_argument('--toplevel-config',
            help="Top-level parameter-framework configuration file. Mandatory.",
            metavar="TOPLEVEL_CONFIG_FILE",
            required=True)
    argparser.add_argument('--criteria',
            help="Criteria file, in '<type> <name> : <value> <value...>' \
        format. Mandatory.",
            metavar="CRITERIA_FILE",
            type=argparse.FileType('r'),
            required=True)
    argparser.add_argument('--initial-settings',
            help="Initial XML settings file (containing a \
        <ConfigurableDomains>  tag",
            nargs='?',
            default=None,
            metavar="XML_SETTINGS_FILE")
    argparser.add_argument('--add-domains',
            help="List of single domain files (each containing a single \
        <ConfigurableDomain> tag",
            metavar="XML_DOMAIN_FILE",
            nargs='*',
            dest='xml_domain_files',
            default=[])
    argparser.add_argument('--add-edds',
            help="List of files in EDD syntax (aka \".pfw\" files)",
            metavar="EDD_FILE",
            type=argparse.FileType('r'),
            nargs='*',
            default=[],
            dest='edd_files')
    argparser.add_argument('--schemas-dir',
            help="Directory of parameter-framework XML Schemas for generation \
        validation",
            default=None)
    argparser.add_argument('--target-schemas-dir',
            help="Ignored. Kept for retro-compatibility")
    argparser.add_argument('--validate',
            help="Validate the settings against XML schemas",
            action='store_true')
    argparser.add_argument('--verbose',
            action='store_true')

    return argparser.parse_args()

def parseCriteria(criteriaFile):
    # Parse a criteria file
    #
    # This file define one criteria per line; they should respect this format:
    #
    # <type> <name> : <values>
    #
    # Where <type> is 'InclusiveCriterion' or 'ExclusiveCriterion';
    #       <name> is any string w/o whitespace
    #       <values> is a list of whitespace-separated values, each of which is any
    #                string w/o a whitespace
    criteria_pattern = re.compile(
        r"^(?P<type>(?:Inclusive|Exclusive)Criterion)\s*" \
        r"(?P<name>\S+)\s*:\s*" \
        r"(?P<values>.*)$")
    criterion_inclusiveness_table = {
        'InclusiveCriterion' : "inclusive",
        'ExclusiveCriterion' : "exclusive"}

    all_criteria = []
    for line_number, line in enumerate(criteriaFile):
        match = criteria_pattern.match(line)
        if not match:
            raise ValueError("The following line is invalid: {}:{}\n{}".format(
                criteriaFile.name, line_number, line))

        criterion_name = match.groupdict()['name']
        criterion_type = match.groupdict()['type']
        criterion_values = re.split("\s*", match.groupdict()['values'])

        criterion_inclusiveness = criterion_inclusiveness_table[criterion_type]

        all_criteria.append({
            "name" : criterion_name,
            "inclusive" : criterion_inclusiveness,
            "values" : criterion_values})

    return all_criteria

def parseEdd(EDDFiles):
    parsed_edds = []

    for edd_file in EDDFiles:
        try:
            root = EddParser.Parser().parse(edd_file)
        except EddParser.MySyntaxError as ex:
            logging.critical(str(ex))
            logging.info("EXIT ON FAILURE")
            exit(2)

        try:
            root.propagate()
        except EddParser.MyPropagationError, ex :
            logging.critical(str(ex))
            logging.info("EXIT ON FAILURE")
            exit(1)

        parsed_edds.append((edd_file.name, root))
    return parsed_edds

def generateDomainCommands(logging, all_criteria, initial_settings, xml_domain_files, parsed_edds):
        # create and inject all the criteria
        logging.info("Creating all criteria")
        for criterion in all_criteria:
            yield ["createSelectionCriterion", criterion['inclusive'],
                   criterion['name']] + criterion['values']

        yield ["start"]

        # Import initial settings file
        if initial_settings:
            logging.info("Importing initial settings file {}".format(initial_settings))
            yield ["importDomainsWithSettingsXML", initial_settings]

        # Import each standalone domain files
        for domain_file in xml_domain_files:
            logging.info("Importing single domain file {}".format(domain_file))
            yield ["importDomainWithSettingsXML", domain_file]

        # Generate the script for each EDD file
        for filename, parsed_edd in parsed_edds:
            logging.info("Translating and injecting EDD file {}".format(filename))
            translator = PfwScriptTranslator()
            parsed_edd.translate(translator)
            for command in translator.getScript():
                yield command

def main():
    logging.root.setLevel(logging.INFO)
    args = parseArgs()

    all_criteria = parseCriteria(args.criteria)

    #
    # EDD files (aka ".pfw" files)
    #
    parsed_edds = parseEdd(args.edd_files)

    # We need to modify the toplevel configuration file to account for differences
    # between development setup and target (installation) setup, in particular, the
    # TuningMwith ode must be enforced, regardless of what will be allowed on the target
    fake_toplevel_config = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".xml",
                                                       prefix="TMPdomainGeneratorPFConfig_")

    install_path = os.path.dirname(os.path.realpath(args.toplevel_config))
    hostConfig.configure(
            infile=args.toplevel_config,
            outfile=fake_toplevel_config,
            structPath=install_path)
    fake_toplevel_config.close()

    # Create the connector. Pipe its input to us in order to write commands;
    # connect its output to stdout in order to have it dump the domains
    # there; connect its error output to stderr.
    connector = subprocess.Popen(["domainGeneratorConnector",
                            fake_toplevel_config.name,
                            'verbose' if args.verbose else 'no-verbose',
                            'validate' if args.validate else 'no-validate',
                            args.schemas_dir],
                           stdout=sys.stdout, stdin=subprocess.PIPE, stderr=sys.stderr)

    initial_settings = None
    if args.initial_settings:
        initial_settings = os.path.realpath(args.initial_settings)

    for command in generateDomainCommands(logging, all_criteria, initial_settings,
                                       args.xml_domain_files, parsed_edds):
        connector.stdin.write('\0'.join(command))
        connector.stdin.write("\n")

    # Closing the connector's input triggers the domain generation
    connector.stdin.close()
    connector.wait()
    os.remove(fake_toplevel_config.name)
    return connector.returncode

# If this file is directly executed
if __name__ == "__main__":
    exit(main())
<|endoftext|>