Skip to content
Snippets Groups Projects
valunifier_tei.py 24.95 KiB
#-*- coding:utf-8 -*-

import datetime

from django.db.models import Count, Min, Max
from lxml import etree
from xml.sax.saxutils import escape

from unifier.models import UnifiedFrame2SlowalFrameMapping, \
                           UnifiedFrameArgumentSlowalFrameMapping
from connections.models import ArgumentConnection, ExampleConnection

from common.management.commands.schema_packer import SchemaPacker

from collections import defaultdict

XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace'


def createteixml(outpath, unified_frames):
    root = write_root()
    write_header(root)
    write_content(root, unified_frames)
    with open(outpath, 'wb') as output_file:
        root.getroottree().write(output_file,
                                 encoding='UTF-8',
                                 pretty_print=True,
                                 xml_declaration=True,
                                 doctype=u'<!DOCTYPE TEI SYSTEM "tei_all.dtd">')

def write_root():
    root = etree.Element('TEI')
    root.attrib[etree.QName(XML_NAMESPACE, 'lang')] = u'pl'
    root.attrib['xmlns'] = u'http://www.tei-c.org/ns/1.0'
    return root
    
def write_header(root, extensions_file=False):
    tei_header = etree.SubElement(root, 'teiHeader')    
    file_desc = etree.SubElement(tei_header, 'fileDesc')
    
    title_stmt = etree.SubElement(file_desc, 'titleStmt')
    title = etree.SubElement(title_stmt, 'title')
    title.text = u'ValUnifier (?)'
    
    publication_stmt = etree.SubElement(file_desc, 'publicationStmt')
    publisher = etree.SubElement(publication_stmt, 'publisher')
    publisher.text = u'Institute of Computer Science, Polish Academy of Sciences (IPI PAN)'
    
    date = etree.SubElement(publication_stmt, 'date')
    date.attrib['when'] = datetime.datetime.now().strftime('%Y-%m-%d')
    
    write_license_elem(publication_stmt)
    
    source_desc = etree.SubElement(file_desc, 'sourceDesc')
    p = etree.SubElement(source_desc, 'p')
    p.text = u'?'

def write_license_elem(parent):
    availability = etree.SubElement(parent, 'availability')
    licence = etree.SubElement(availability, 'licence')
    licence.attrib['target'] = u'http://creativecommons.org/licenses/by-sa/4.0/'
    
    p = etree.SubElement(licence, 'p')
    p.text = u'(C) Copyright 2012–2018 by the Institute of Computer Science, Polish Academy of Sciences (IPI PAN)'
    
    p = etree.SubElement(licence, 'p')
    p.text = u'This work is distributed under a CC BY-SA license: http://creativecommons.org/licenses/by-sa/4.0/'
    
    p = etree.SubElement(licence, 'p')
    p.text = '?'

def write_content(root, unified_frames):
    text = etree.SubElement(root, 'text')
    body = etree.SubElement(text, 'body')
    schemata = etree.SubElement(body, 'div')
    frames = etree.SubElement(body, 'div')
    used_schemata = SchemaPacker()
    write_unified_frames(frames, unified_frames, used_schemata)
    write_used_schemata(schemata, used_schemata)

#=================== DIV -- SEMANTIC FRAMES ===================#
    
def write_unified_frames(parent, unified_frames, used_schemata):
    frames_head = etree.SubElement(parent, 'head')
    frames_head.text = 'Semantic Frames'

    for unified_frame in unified_frames:
        write_unified_frame_entry(parent, unified_frame, used_schemata)

def write_unified_frame_entry(parent, unified_frame, used_schemata):
    entry_xml_id = u'unif_%d-ent' % unified_frame.id
    entry = etree.SubElement(parent, 'entry')
    entry.attrib[etree.QName(XML_NAMESPACE, 'id')] = entry_xml_id

    write_unified_frame_definition(entry, unified_frame)

    write_status_info(entry, unified_frame)
    
    write_unified_frame(entry, unified_frame)
    
    write_unified_frames_realizations(entry, unified_frame, used_schemata)

def write_unified_frame_definition(entry, unified_frame):
    definition = etree.SubElement(entry, 'def')
    definition.text = unified_frame.title

def write_status_info(parent, unified_frame): 
    general_fs = etree.SubElement(parent, 'fs')
    general_fs.attrib['type'] = 'general_info'
    status_f =  etree.SubElement(general_fs, 'f')
    status_f.attrib['name'] = 'status'
    status_string = etree.SubElement(status_f, 'string')
    status_string.text = unified_frame.status

#=================== FS TYPE = "FRAME" ===================#
    
def write_unified_frame(parent, unified_frame):
    frame_xml_id = u'unif_%d-frm' % unified_frame.id
    
    frame_fs = etree.SubElement(parent, 'fs')
    frame_fs.attrib[etree.QName(XML_NAMESPACE, 'id')] = frame_xml_id
    frame_fs.attrib['type'] = 'frame'
    
    write_frame_arguments(frame_fs, unified_frame)
    
def write_frame_arguments(parent, unified_frame): 
    arguments_f = etree.SubElement(parent, 'f')
    arguments_f.attrib['name'] = 'arguments'
    
    vColl = etree.SubElement(arguments_f, 'vColl')
    vColl.attrib['org'] = 'set'
    
    for arg in unified_frame.sorted_arguments():
        write_frame_argument(vColl, unified_frame, arg)

def write_frame_argument(parent, frame, arg):
    arg_base_id = u'unif_%d' % frame.id
    arg_xml_id = arg_base_id + u'.%d-arg' % arg.id
    
    argument_fs = etree.SubElement(parent, 'fs')
    argument_fs.attrib[etree.QName(XML_NAMESPACE, 'id')] = arg_xml_id
    argument_fs.attrib['type'] = 'argument'

    write_roles(argument_fs, arg)
    write_selectional_preferences(argument_fs, arg, arg_base_id)
    
def write_roles(parent, arg):
    if arg.role_type is not None:
        role_type_f = etree.SubElement(parent, 'f')
        role_type_f.attrib['name'] = 'role_type'
        role_type_symbol = etree.SubElement(role_type_f, 'symbol')
        role_type_symbol.attrib['value'] = arg.role_type.type

    role_f = etree.SubElement(parent, 'f')
    role_f.attrib['name'] = 'role'
    role_symbol = etree.SubElement(role_f, 'symbol')
    if arg.role is None:
        role_symbol.attrib['value'] = '/'.join([r.role.role for r in arg.proposed_roles.all()])
    else:
        role_symbol.attrib['value'] = arg.role.role.role

        if arg.role.attribute is not None and arg.role.sub_attribute is not None:
            attributes_f = etree.SubElement(parent, 'f')
            attributes_f.attrib['name'] = 'arributes'
            vColl = etree.SubElement(attributes_f, 'vColl')
            vColl.attrib['org'] = 'set'
            if arg.role.attribute is not None:
                write_role_attribute(vColl, arg.role.attribute.attribute)        
            if arg.role.sub_attribute is not None:
                write_role_attribute(vColl, arg.role.sub_attribute.sub_attribute)

def write_role_attribute(parent, symbol):
    attribute_symbol = etree.SubElement(parent, 'symbol')
    attribute_symbol.attrib['value'] = symbol
            
def write_selectional_preferences(parent, arg, arg_base_id):
    if len(arg.predefined.all()) > 0 or\
       len(arg.synsets.all()) > 0 or\
       len(arg.relations.all()) > 0:
        sel_prefs_f = etree.SubElement(parent, 'f')
        sel_prefs_f.attrib['name'] = 'sel_prefs'
    
        sel_prefs_groups_fs = etree.SubElement(sel_prefs_f, 'fs')
        sel_prefs_groups_fs.attrib['type'] = 'sel_prefs_groups'
        
        write_synsets_selprefs(sel_prefs_groups_fs, arg)
        write_predefined_selprefs(sel_prefs_groups_fs, arg)
        write_relation_selprefs(sel_prefs_groups_fs, arg, arg_base_id)
    
def write_synsets_selprefs(parent, arg):
    synsets = arg.synsets.all()
    if len(synsets) > 0:
        synsets_f = etree.SubElement(parent, 'f')
        synsets_f.attrib['name'] = 'synsets'
        
        vColl = etree.SubElement(synsets_f, 'vColl')
        vColl.attrib['org'] = 'set'
        
        for synset in synsets:
            write_synset(vColl, synset)
            
def write_synset(parent, synset):
    id_numeric = etree.SubElement(parent, 'numeric')
    id_numeric.attrib['value'] = str(synset.id)
            
def write_predefined_selprefs(parent, arg):
    predefs = arg.predefined.all()
    if len(predefs) > 0:
        predefs_f = etree.SubElement(parent, 'f')
        predefs_f.attrib['name'] = 'predefs'
        
        vColl = etree.SubElement(predefs_f, 'vColl')
        vColl.attrib['org'] = 'set'
        
        for predef in predefs:
            write_predef(vColl, predef)
            
def write_predef(parent, predef):
    name_symbol = etree.SubElement(parent, 'symbol')
    name_symbol.attrib['value'] = predef.name
    
def write_relation_selprefs(parent, arg, arg_base_id):
    relations = arg.relations.all()
    if len(relations) > 0:
        relations_f = etree.SubElement(parent, 'f')
        relations_f.attrib['name'] = 'relations'
        
        vColl = etree.SubElement(relations_f, 'vColl')
        vColl.attrib['org'] = 'set'
        
        for relation in relations:
            write_relation(vColl, relation, arg_base_id)
            
def write_relation(parent, relation, arg_base_id):
    relation_fs = etree.SubElement(parent, 'fs')
    relation_fs.attrib['type'] = 'relation'
    
    relation_f = etree.SubElement(relation_fs, 'f')
    relation_f.attrib['name'] = 'type'
    type_symbol = etree.SubElement(relation_f, 'symbol')
    type_symbol.attrib['value'] = relation.relation.key
    
    to_f = etree.SubElement(relation_fs, 'f')
    to_f.attrib['name'] = 'to'
    to_xml_link = '#%s.%d-arg' % (arg_base_id, relation.to.id)
    arg_link = etree.SubElement(to_f, 'fs')
    arg_link.attrib['sameAs'] = to_xml_link
    arg_link.attrib['type'] = 'argument'

#=================== FS TYPE = "FRAME_REALIZATIONS" ===================#
    
def write_unified_frames_realizations(entry, unified_frame, used_schemata):
    realizations_fs = etree.SubElement(entry, 'fs')
    realizations_fs.attrib['type'] = 'frame_realizations'

    realizations_f = etree.SubElement(realizations_fs, 'f')
    realizations_f.attrib['name'] = 'realizations'

    vColl = etree.SubElement(realizations_f, 'vColl')
    vColl.attrib['org'] = 'set'
    
    write_lexical_units_realizations(vColl, unified_frame, used_schemata)

def write_lexical_units_realizations(parent, unified_frame, used_schemata):
    frames_mappings = UnifiedFrame2SlowalFrameMapping.objects.filter(unified_frame=unified_frame,\
                                                                     removed=False)
    
    lexical_units_sortable = {}
    lexical_unit_mapping = {}
    for mapping in frames_mappings:
        frame = mapping.slowal_frame
        for lexical_unit in frame.lexical_units.all():
            lexical_unit_mapping[lexical_unit] = mapping
            lexical_units_sortable[(lexical_unit.base, lexical_unit.sense)] = lexical_unit

    for signature in sorted(lexical_units_sortable.keys()):
        lexical_unit = lexical_units_sortable[signature]
        mapping = lexical_unit_mapping[lexical_unit]
        write_lexical_unit_realization(parent, unified_frame, lexical_unit, mapping, used_schemata)
        
def write_lexical_unit_realization(parent, unified_frame, lexical_unit, mapping, used_schemata):
    lexical_unit_realizations_fs = etree.SubElement(parent, "fs")
    lexical_unit_realizations_fs.attrib['type'] = "lexical_unit_realizations"

    lexical_unit_f = etree.SubElement(lexical_unit_realizations_fs, "f")
    lexical_unit_f.attrib['name'] = 'lexical_unit'
    write_lexical_unit(lexical_unit_f, lexical_unit, mapping)

    syntactic_realizations_f = etree.SubElement(lexical_unit_realizations_fs, 'f')
    syntactic_realizations_f.attrib['name'] = 'syntactic_realizations'

    vColl = etree.SubElement(syntactic_realizations_f, 'vColl')
    vColl.attrib['org'] = 'set'
    write_alternations(vColl, unified_frame, lexical_unit, mapping, used_schemata)

def write_lexical_unit(parent, lexical_unit, mapping):
    meaning_fs = etree.SubElement(parent, 'fs')
    meaning_fs.attrib['type'] = 'lexical_unit'
    
    name_f = etree.SubElement(meaning_fs, 'f')
    name_f.attrib['name'] = 'name'
    name_content = etree.SubElement(name_f, 'string')
    name_content.text = lexical_unit.base
    
    variant_f = etree.SubElement(meaning_fs, 'f')
    variant_f.attrib['name'] = 'variant'
    variant_string = etree.SubElement(variant_f, 'string')
    variant_string.text = lexical_unit.sense

    if lexical_unit.luid is not None:
        plwnluid_f = etree.SubElement(meaning_fs, 'f')
        plwnluid_f.attrib['name'] = 'plwnluid'
        plwnluid_numeric = etree.SubElement(plwnluid_f, 'numeric')
        plwnluid_numeric.attrib['value'] = str(lexical_unit.luid)
        
    if lexical_unit.synset is not None and lexical_unit.synset.id > 0:
        plwnsid_f = etree.SubElement(meaning_fs, 'f')
        plwnsid_f.attrib['name'] = 'plwnsid'
        plwnsid_numeric = etree.SubElement(plwnsid_f, 'numeric')
        plwnsid_numeric.attrib['value'] = str(lexical_unit.synset.id)

    slowal_frame = mapping.slowal_frame
    if slowal_frame.opinion is not None:
        opinion_f = etree.SubElement(meaning_fs, 'f')
        opinion_f.attrib['name'] = 'opinion'
        opinion_symbol = etree.SubElement(opinion_f, 'symbol')
        opinion_symbol.attrib['value'] = slowal_frame.opinion.key
        
def write_alternations(parent, unified_frame, lexical_unit, mapping, used_schemata):
    alternations = prepare_alternations(lexical_unit, mapping, used_schemata)
    for key in sorted(alternations.keys()):
        alternation_fs = etree.SubElement(parent, 'fs')
        alternation_fs.attrib['type'] = 'aternation'
        connections_f = etree.SubElement(alternation_fs, 'f')
        connections_f.attrib['name'] = 'connections'
        vColl = etree.SubElement(connections_f, 'vColl')
        vColl.attrib['org'] = 'set'
        for argument, schema_hooks in alternations[key].items():
            connection_fs = etree.SubElement(vColl, 'fs')
            connection_fs.attrib['type'] = 'connection'
            argument_f = etree.SubElement(connection_fs, 'f')
            argument_f.attrib['name'] = 'argument'
            argument_fs = etree.SubElement(argument_f, 'fs')
            argument_fs.attrib['type'] = 'argument'
            argument_fs.attrib['sameAs'] = u'#unif_%d.%d-arg' % (unified_frame.id, argument.id)
            phrases_f = etree.SubElement(connection_fs, 'f')
            write_phrases_coll(phrases_f, schema_hooks, used_schemata)

def prepare_alternations(lexical_unit, mapping, used_schemata):
    connections_info = analyse_connections(mapping)
    
    argument_mappings = UnifiedFrameArgumentSlowalFrameMapping.objects.filter(unified_frame_mapping = mapping)
    alternations = defaultdict(lambda: defaultdict(lambda: []))
    for argument_mapping in argument_mappings:
        uargument = argument_mapping.unified_agrument
        sargument = argument_mapping.slowal_agrument

        success = False
        try:
            argument_realization = ArgumentConnection.objects.get(argument = sargument)
            success = True
        except:
            print(sargument.id)
            
        if success:
            by_schema_realizations = argument_realization.schema_connections.all()
            for schema_hook in by_schema_realizations:
                if valid_connection(lexical_unit, schema_hook, connections_info):
                    subentry = schema_hook.subentry
                    schema = schema_hook.schema
                    used_schemata.add(subentry, schema)
                    alternation = schema_hook.alternation
                    alternations[(subentry.id, schema.id, alternation)][uargument].append(schema_hook)

    return alternations

def analyse_connections(mapping):
    sframe = mapping.slowal_frame
    lus = sframe.lexical_units.all()
    lu_schema_connections = defaultdict(lambda: set())
    connected_lu_count = defaultdict(lambda: 0)
    for lu in lus:
        examples = ExampleConnection.objects.filter(lexical_unit=lu)
        for example in examples:
            scs = example.schema_connections.all()
            if len(scs) > 0:
                schema_hook = scs[0]
                schema_id = (schema_hook.subentry.id, schema_hook.schema.id)
                lu_schema_connections[lu].add(schema_id)
                connected_lu_count[schema_id] += 1
    return (lu_schema_connections, connected_lu_count)

def valid_connection(lexical_unit, schema_hook, connections_info):
    schema_id = (schema_hook.subentry.id, schema_hook.schema.id)
    lu_schema_connections, connected_lu_count = connections_info
    if schema_id in lu_schema_connections[lexical_unit]:
        return True
    if connected_lu_count[schema_id] == 0:
        in_lemma = False
        in_lemma |= (' się ' in lexical_unit.base)
        in_lemma |= (lexical_unit.base.endswith(' się'))
        in_schema = False
        in_schema |= (schema_hook.subentry.inherent_sie.name == 'true')
        for position in schema_hook.schema.positions.all():
            if position.phrases_count == 1:
                phrase_type = position.phrase_types.all()[0].main_type 
                in_schema |= (phrase_type == 'refl')
                in_schema |= (phrase_type == 'recip')
        return (in_lemma == in_schema)
    return False

def write_phrases_coll(parent, phrases_list, used_schemata):
    vColl = etree.SubElement(parent, 'vColl')
    vColl.attrib['org'] = 'set'
    for phrase in phrases_list:
        phrase_fs = etree.SubElement(vColl, 'fs')
        phrase_fs.attrib['type'] = 'phrase'
        subentry_id, schema_id = used_schemata.get_ids(phrase.subentry, phrase.schema)
        phrase_fs.attrib['sameAs'] = u'#unif_%d.%d.%d.%d-phr' %(subentry_id, schema_id, phrase.position.id, phrase.phrase_type.id)
        
    
#=================== DIV -- SYNTACTIC SCHEMATA ===================#

def write_used_schemata(parent, used_schemata):
    schemata_head = etree.SubElement(parent, 'head')
    schemata_head.text = 'Syntactic Schemata'

    for subentry_id in used_schemata.unique_schemas:
        for schema_id in used_schemata.unique_schemas[subentry_id]:
            subentry, schema = used_schemata.unique_schemas[subentry_id][schema_id]
            write_schema_entry(parent, subentry, subentry_id, schema, schema_id)
            

def write_schema_entry(parent, subentry, subentry_id, schema, schema_id):
    entry_xml_id = u'unif_%d.%d-schent' %(subentry_id, schema_id)
    entry = etree.SubElement(parent, 'entry')
    entry.attrib[etree.QName(XML_NAMESPACE, 'id')] = entry_xml_id

    write_schema_definition(entry, subentry, subentry_id, schema, schema_id)

    write_schema(entry, subentry, subentry_id, schema, schema_id)

def write_schema_definition(parent, subentry, subentry_id, schema, schema_id):
    d = etree.SubElement(parent, 'def')
    d.text = schema_textual_representation(subentry, schema)

def schema_textual_representation(subentry, schema):
    result = ''

    if subentry.inherent_sie.name == 'true':
        result += ' się:'
    else:
        rerult += ':'
    
    opinion = schema.opinion.key
    if opinion == 'vul':
        result += ' wulgarny:'
    elif opinion == 'col':
        result += ' potoczny:'
    elif opinion == 'dat':
        result += ' archaiczny:'
    elif opinion == 'bad':
        result += ' zły:'
    elif opinion == 'unc':
        result += ' wątpliwy:'
    elif opinion == 'cer':
        result += ' pewny:'
    else:
        result += ' brak:'

    if subentry.negativity is not None:
        result += ' ' + subentry.negativity.name + ':'
    else:
        result += ' :'

    if subentry.predicativity.name == 'true':
        result += ' pred:'
    else:
        rerult += ':'

    if subentry.aspect is not None:
        result += ' ' + subentry.aspect.name + ':'
    else:
        result += ' :'

    positions_rep = []
    for position in schema.positions.all():
        positions_rep.append(str(position))

    result += ' ' + ' + '.join(positions_rep)

    return result
        
def write_schema(parent, subentry, subentry_id, schema, schema_id):
    schema_xml_id = u'unif_%d.%d-sch' %(subentry_id, schema_id)

    schema_fs = etree.SubElement(parent, 'fs')
    schema_fs.attrib[etree.QName(XML_NAMESPACE, 'id')] = schema_xml_id
    schema_fs.attrib['type'] = 'schema'

    # textual representation @TODO -- not present in the database
    # text_rep_f_elem = etree.SubElement(schema_fs_elem, 'f')
    # text_rep_f_elem.attrib['name'] = 'text_rep'
    # text_rep_string = etree.SubElement(text_rep_f_elem, 'string')
    # text_rep = schema.get_position_spaced_text_rep()
    # if schema.characteristics.filter(type=u'ZWROTNOŚĆ', value__value=u'się').exists():
    #     text_rep = ' ' + text_rep
    # text_rep_string.text = lemma.entry_obj.name + text_rep.replace(':',': ')

    # schema opinion
    schema_opinion = schema.opinion.key
    if schema.opinion.key is None:
        schema_opinion = 'unk'
    opinion_f = etree.SubElement(schema_fs, 'f')
    opinion_f.attrib['name'] = 'opinion'
    opinion_symbol = etree.SubElement(opinion_f, 'symbol')
    opinion_symbol.attrib['value'] = schema_opinion

    # inherent "się"
    reflex = subentry.inherent_sie.name
    selfmark_f = etree.SubElement(schema_fs, 'f')
    selfmark_f.attrib['name'] = 'inherent_sie'
    selfmark_binary = etree.SubElement(selfmark_f, 'binary')
    selfmark_binary.attrib['value'] = reflex
    
    # aspect
    aspect_f = etree.SubElement(schema_fs, 'f')
    aspect_f.attrib['name'] = 'aspect'
    if subentry.aspect is not None and subentry.aspect.name != '':
        aspect_symbol = etree.SubElement(aspect_f, 'symbol')
        aspect_symbol.attrib['value'] = subentry.aspect.name
    
    # negativity
    negativity_f = etree.SubElement(schema_fs, 'f')
    negativity_f.attrib['name'] = 'negativity'
    if subentry.negativity is not None and subentry.negativity.name != '':
        negativity_symbol = etree.SubElement(negativity_f, 'symbol')
        negativity_symbol.attrib['value'] = subentry.negativity.name
    
    # predicativity
    predicativity = subentry.predicativity.name
    predicativity_f = etree.SubElement(schema_fs, 'f')
    predicativity_f.attrib['name'] = 'predicativity'
    predicativity_binary = etree.SubElement(predicativity_f, 'binary')
    predicativity_binary.attrib['value'] = predicativity

    # positions
    write_positions(schema_fs, subentry_id, schema, schema_id)

def write_positions(parent, subentry_id, schema, schema_id):
    positions = schema.positions.all()
    positions_f = etree.SubElement(parent, 'f')
    positions_f.attrib['name'] = 'positions'
    vColl = etree.SubElement(positions_f, 'vColl')
    vColl.attrib['org'] = 'set'
    for position in positions:
        write_position(vColl, subentry_id, schema_id, position)
        
def write_position(parent, subentry_id, schema_id, position):
    position_xml_id = u'unif_%d.%d.%d-psn' %(subentry_id, schema_id, position.id)
    position_fs = etree.SubElement(parent, 'fs')
    position_fs.attrib['type'] = 'position'
    position_fs.attrib[etree.QName(XML_NAMESPACE, 'id')] = position_xml_id

    text_rep_f = etree.SubElement(position_fs, 'f')
    text_rep_f.attrib['name'] = 'textual_representation'
    text_rep_string = etree.SubElement(text_rep_f, 'string')
    text_rep_string.text = str(position)
    
    write_function(position_fs, position)
    
    write_control(position_fs, position)
    
    write_phrases(position_fs, subentry_id, schema_id, position)

def write_function(parent, position):
    function = position.function
    if function is not None:
        function_f = etree.SubElement(parent, 'f')
        function_f.attrib['name'] = 'function'
        function_symbol = etree.SubElement(function_f, 'symbol')
        function_symbol.attrib['value'] = function.name
    
def write_control(parent, position):
    control = position.control
    pred_control = position.pred_control
    if control is not None or pred_control is not None:
        control_f = etree.SubElement(parent, 'f')
        control_f.attrib['name'] = 'control'
        vColl = etree.SubElement(control_f, 'vColl')
        vColl.attrib['org'] = 'set'
        if control is not None:
            control = control.name
            control_symbol = etree.SubElement(vColl, 'symbol')
            control_symbol.attrib['value'] = control
        if pred_control is not None:
            control = pred_control.name
            pred_control_symbol = etree.SubElement(vColl, 'symbol')
            pred_control_symbol.attrib['value'] = control

def write_phrases(parent, subentry_id, schema_id, position):
    phrases = position.phrase_types.all()
    phrases_f = etree.SubElement(parent, 'f')
    phrases_f.attrib['name'] = 'phrases'
    vColl = etree.SubElement(phrases_f, 'vColl')
    vColl.attrib['org'] = 'set'
    for phrase in phrases:
        write_phrase(vColl, subentry_id, schema_id, position, phrase)
    
def write_phrase(parent, subentry_id, schema_id, position, phrase):
    phrase_xml_id = u'unif_%d.%d.%d.%d-phr' %(subentry_id, schema_id, position.id, phrase.id)
    phrase_fs = etree.SubElement(parent, 'fs')
    phrase_fs.attrib[etree.QName(XML_NAMESPACE, 'id')] = phrase_xml_id
    phrase_fs.attrib['type'] = phrase.main_type.name
    # @TODO -- currently no expansions file
    # if phrase.realizations.exists() and write_expansions_id:
    #     write_expansions_link(phrase_fs_elem, phrase)
    write_phrase_textrep(phrase_fs, phrase)
            
def write_phrase_textrep(parent, phrase):
    text_rep = phrase.text_rep
    textrep_f = etree.SubElement(parent, 'f')
    textrep_f.attrib['name'] = 'textual_representation'
    textrep_string = etree.SubElement(textrep_f, 'string')
    textrep_string.text = text_rep