Commit 7c0a78887cfb7c3f89ded4166495ad23085050cc

Authored by Bartłomiej Nitoń
1 parent 9e0dc4e9

Added new control types.

dictionary/management/commands/add_new_control_types.py 0 → 100644
  1 +# -*- coding:utf-8 -*-
  2 +
  3 +from django.core.management.base import BaseCommand
  4 +
  5 +from dictionary.models import POS, PositionCategory
  6 +
  7 +
  8 +class Command(BaseCommand):
  9 +
  10 + def handle(self, **options):
  11 + add_new_controll_types()
  12 +
  13 +
  14 +def add_new_controll_types():
  15 + poss = POS.objects.all()
  16 +
  17 + pred_controller, xx = PositionCategory.objects.get_or_create(category='pred_controller',
  18 + control=True,
  19 + priority=70)
  20 + for pos in poss:
  21 + if pos.tag != 'noun':
  22 + pred_controller.poss.add(pos)
  23 +
  24 + pred_controllee, xx = PositionCategory.objects.get_or_create(category='pred_controllee',
  25 + control=True,
  26 + priority=80)
  27 + for pos in poss:
  28 + if pos.tag != 'noun':
  29 + pred_controllee.poss.add(pos)
... ...
dictionary/management/commands/change_control2pred_control.py 0 → 100644
  1 +# -*- coding:utf-8 -*-
  2 +
  3 +from django.contrib.auth.models import User
  4 +from django.core.management.base import BaseCommand
  5 +
  6 +from dictionary.models import Change, Frame, Frame_Opinion, Lemma, Position, PositionCategory, \
  7 + get_or_create_nkjp_arg_selection, get_or_create_nkjp_example, get_ready_statuses, sortArguments, \
  8 + sortFrameChars, sortPosCatsAsStrTab, sortPositions
  9 +from dictionary.saving import create_argument_ref, create_operation, create_phrase_type_ref, \
  10 + reconnect_examples, update_connections
  11 +from semantics.models import LexicalUnitExamples
  12 +
  13 +
  14 +class Command(BaseCommand):
  15 +
  16 + def handle(self, **options):
  17 + self.print_schema4manual_change()
  18 + self.change_control2pred_control()
  19 +
  20 + def print_schema4manual_change(self):
  21 + ready_statuses = get_ready_statuses()
  22 + for lemma in Lemma.objects.filter(old=False, status__in=ready_statuses).order_by('entry_obj__name'):
  23 + for schema in lemma.frames.all():
  24 + for position in schema.positions.all():
  25 + if self.position_need_manual_change(position):
  26 + print (lemma, schema.pk)
  27 + break
  28 +
  29 + def position_need_manual_change(self, position):
  30 + if position.categories.filter(category='controllee').exists():
  31 + for phrase_type in position.arguments.all():
  32 + if self.phrase_type_is_infp(phrase_type):
  33 + return True
  34 + return False
  35 +
  36 + def phrase_type_is_infp(self, phrase_type):
  37 + if (phrase_type.type == 'infp'):
  38 + return True
  39 + if (phrase_type.type == 'lex' and
  40 + phrase_type.atributes.get(type='TYP FRAZY').values.all()[0].argument.type == 'infp'):
  41 + return True
  42 + return False
  43 +
  44 + def change_control2pred_control(self):
  45 + ready_statuses = get_ready_statuses()
  46 + for lemma in Lemma.objects.filter(old=False, status__in=ready_statuses).order_by('entry_obj__name'):
  47 + print (lemma)
  48 + changes = {'schemata': [],
  49 + 'schemata2change': []}
  50 + for schema in lemma.frames.all():
  51 + if self.schema_need_auto_change(schema):
  52 + changes['schemata2change'].append(schema)
  53 + else:
  54 + changes['schemata'].append(schema)
  55 +
  56 + if changes['schemata2change']:
  57 + self.save_new_lemma_version(lemma, changes)
  58 +
  59 + def schema_need_auto_change(self, schema):
  60 + for position in schema.positions.all():
  61 + if self.controllee_need_auto_change(position):
  62 + return True
  63 + return False
  64 +
  65 + def controllee_need_auto_change(self, position):
  66 + auto_change = False
  67 + if position.categories.filter(category__startswith='controllee').exists():
  68 + auto_change = True
  69 + for phrase_type in position.arguments.all():
  70 + if self.phrase_type_is_infp(phrase_type):
  71 + auto_change = False
  72 + return auto_change
  73 +
  74 + def save_new_lemma_version(self, old_lemma, changes):
  75 + admin_user = User.objects.get(username='bniton')
  76 +
  77 + old_lemma.old = True
  78 + old_lemma.save()
  79 +
  80 + # tworzenie nowej wersji hasla
  81 + new_lemma = Lemma(entry=old_lemma.entry_obj.name,
  82 + entry_obj=old_lemma.entry_obj,
  83 + owner=old_lemma.owner,
  84 + phraseologist=old_lemma.phraseologist,
  85 + semanticist=old_lemma.semanticist,
  86 + vocabulary=old_lemma.vocabulary,
  87 + status=old_lemma.status,
  88 + old=False,
  89 + frequency_1M=old_lemma.frequency_1M,
  90 + frequency_300M=old_lemma.frequency_300M)
  91 + new_lemma.save()
  92 +
  93 + # tworzenie zmiany do systemu kontroli zmian
  94 + if (old_lemma.owner):
  95 + lemma_change = Change(user=admin_user, entry=old_lemma, act_owner=old_lemma.owner)
  96 + lemma_change.save()
  97 + else:
  98 + lemma_change = Change(user=admin_user, entry=old_lemma)
  99 + lemma_change.save()
  100 +
  101 + # przepisywanie starych wersji dla kontroli zmian i dodanie nowej
  102 + for version in old_lemma.old_versions.all():
  103 + new_lemma.old_versions.add(version)
  104 + new_lemma.old_versions.add(lemma_change)
  105 +
  106 + # przepisywanie historii zmian statusow
  107 + for status_change in old_lemma.status_history.all():
  108 + new_lemma.status_history.add(status_change)
  109 +
  110 + # przepisywanie wiadomosci
  111 + for message in old_lemma.messages.all():
  112 + new_lemma.messages.add(message)
  113 +
  114 + # przepisywanie starych ramek
  115 + for old_frame in old_lemma.old_frames.all():
  116 + new_lemma.old_frames.add(old_frame)
  117 +
  118 + # przepisywanie ramek skladnicowych
  119 + for skladnica_frame in old_lemma.skladnica_frames.all():
  120 + new_lemma.skladnica_frames.add(skladnica_frame)
  121 +
  122 + # przepisywanie ramek B
  123 + for B_frame in old_lemma.B_frames.all():
  124 + new_lemma.B_frames.add(B_frame)
  125 +
  126 + # przepisywanie opinii o schematach
  127 + for schema_opinion in old_lemma.frame_opinions.all():
  128 + new_lemma.frame_opinions.add(schema_opinion)
  129 +
  130 + # przepisywanie przykladow niepasujacych do zadnego schematu
  131 + for example in old_lemma.lemma_nkjp_examples.all():
  132 + new_lemma.lemma_nkjp_examples.add(example)
  133 +
  134 + # dodawanie niezmienionych schematow
  135 + for schema in changes['schemata']:
  136 + new_lemma.frames.add(schema)
  137 +
  138 + # tworzenie nowych schematow i dolaczanie ich do czasownika
  139 + schemata_conversions = []
  140 + for old_schema in changes['schemata2change']:
  141 + new_schema, positions_changes = self.get_or_create_new_schema(old_schema)
  142 +
  143 + # przepinanie opinii o ramce
  144 + try:
  145 + old_opinion = old_lemma.frame_opinions.get(frame=old_schema)
  146 + opinion_value = old_opinion.value
  147 + new_lemma.frame_opinions.remove(old_opinion)
  148 + try:
  149 + new_opinion = Frame_Opinion.objects.get(frame=new_schema,
  150 + value=opinion_value)
  151 + except Frame_Opinion.DoesNotExist:
  152 + new_opinion = Frame_Opinion(frame=new_schema,
  153 + value=opinion_value)
  154 + new_opinion.save()
  155 + new_lemma.frame_opinions.add(new_opinion)
  156 + except Frame_Opinion.DoesNotExist:
  157 + pass
  158 +
  159 + new_lemma.frames.add(new_schema)
  160 + schemata_conversions.append({'old_schema': old_schema,
  161 + 'new_schema': new_schema,
  162 + 'positions_changes': positions_changes})
  163 +
  164 + # przepisywanie semantyki
  165 + sem_reconnect_operations = self.get_semantic_operations(new_lemma, schemata_conversions)
  166 + update_connections(new_lemma.id, sem_reconnect_operations, admin_user)
  167 +
  168 + # przepisywanie/dodawanie nowych przykladow do schematow
  169 + examples_operations = []
  170 + for old_example in old_lemma.nkjp_examples.all():
  171 + if new_lemma.frames.filter(pk=old_example.frame.pk).exists():
  172 + new_lemma.nkjp_examples.add(old_example)
  173 + else:
  174 + conversion = (conv for conv in schemata_conversions if conv['old_schema'] == old_example.frame).next()
  175 +
  176 + argument_selections = []
  177 + for old_arg_selection in old_example.arguments.all():
  178 +
  179 + position_conversion = next((pos_conv for pos_conv in conversion['positions_changes']
  180 + if pos_conv['from'].pk == old_arg_selection.position.pk), None)
  181 +
  182 + if position_conversion:
  183 + new_arg_selection, xx = get_or_create_nkjp_arg_selection(position_conversion['to'],
  184 + old_arg_selection.arguments.all())
  185 + argument_selections.append(new_arg_selection)
  186 + else:
  187 + argument_selections.append(old_arg_selection)
  188 +
  189 + # sprawdzanie czy dany obiekt klasy NKJP_Example istnieje
  190 + new_example, xx = get_or_create_nkjp_example(conversion['new_schema'], argument_selections,
  191 + old_example.sentence, old_example.source,
  192 + old_example.comment, old_example.opinion,
  193 + old_example.approvers.all(),
  194 + old_example.approved, old_example.semantic)
  195 + new_lemma.nkjp_examples.add(new_example)
  196 +
  197 + # reconnect examples in semantic layer
  198 + for frame in new_lemma.entry_obj.visible_frames(): # czy actual ??
  199 + for lu in frame.lexical_units.all():
  200 + if LexicalUnitExamples.objects.filter(lexical_unit=lu, example=old_example).exists():
  201 + examples_operations.append(self.disconnect_example_operation(lu, old_example))
  202 + examples_operations.append(self.connect_example_operation(lu, new_example))
  203 +
  204 + reconnect_examples(new_lemma, examples_operations)
  205 +
  206 + def get_or_create_new_schema(self, old_schema):
  207 + positions = []
  208 + positions_changes = []
  209 + for position in old_schema.positions.all():
  210 + if self.position_need_manual_change(position):
  211 + new_position = self.get_or_create_new_position(position)
  212 + positions.append(new_position)
  213 + positions_changes.append({'from': position, 'to': new_position})
  214 + else:
  215 + positions.append(position)
  216 +
  217 + sorted_positions = []
  218 + sorted_positions_dict = sortPositions(positions)
  219 + for position_dict in sorted_positions_dict:
  220 + sorted_positions.append(position_dict['position'])
  221 +
  222 + sorted_positions_strs = []
  223 + for position in sorted_positions:
  224 + sorted_positions_strs.append(position.text_rep)
  225 +
  226 + sorted_schema_chars = sortFrameChars(old_schema.characteristics.all())
  227 + sorted_schema_chars_strs = [char.value.value for char in sorted_schema_chars]
  228 +
  229 + text_rep = u'%s:%s' % (':'.join(sorted_schema_chars_strs),
  230 + '+'.join(sorted_positions_strs))
  231 +
  232 + try:
  233 + new_schema = Frame.objects.get(text_rep=text_rep)
  234 + except Frame.DoesNotExist:
  235 + new_schema = Frame(text_rep=text_rep)
  236 + new_schema.save()
  237 +
  238 + last_pos_obj = None
  239 + pos_obj_count = 0
  240 + for pos_obj in sorted_positions:
  241 + same_pos_db = Position.objects.filter(text_rep=pos_obj.text_rep).order_by('id')
  242 + if not last_pos_obj or last_pos_obj.text_rep != pos_obj.text_rep:
  243 + pos_obj_count = 1
  244 + new_schema.positions.add(same_pos_db[0])
  245 + else:
  246 + pos_obj_count = pos_obj_count + 1
  247 + if pos_obj_count <= len(same_pos_db):
  248 + same_pos_obj = same_pos_db[pos_obj_count - 1]
  249 + new_schema.positions.add(same_pos_obj)
  250 + else:
  251 + same_pos_obj = Position(text_rep=pos_obj.text_rep)
  252 + same_pos_obj.save()
  253 + for category in pos_obj.categories.all():
  254 + same_pos_obj.categories.add(category)
  255 + for arg in pos_obj.arguments.all():
  256 + same_pos_obj.arguments.add(arg)
  257 + new_schema.positions.add(same_pos_obj)
  258 + last_pos_obj = pos_obj
  259 + for schema_char in old_schema.characteristics.all():
  260 + new_schema.characteristics.add(schema_char)
  261 + if new_schema.has_phraseologic_arguments():
  262 + new_schema.phraseologic = True
  263 + new_schema.save()
  264 +
  265 + return new_schema, positions_changes
  266 +
  267 + def position_need_manual_change(self, position):
  268 + if position.categories.filter(category__in=['controllee', 'controller']).exists():
  269 + return True
  270 + return False
  271 +
  272 + def get_or_create_new_position(self, old_position):
  273 + categories_strs = []
  274 + for category in old_position.categories.all():
  275 + if category.category == 'controllee':
  276 + categories_strs.append('pred_controllee')
  277 + elif category.category == 'controller':
  278 + categories_strs.append('pred_controller')
  279 + else:
  280 + categories_strs.append(category.category)
  281 +
  282 + sorted_categories_strs = sortPosCatsAsStrTab(categories_strs)
  283 + sorted_arguments = sortArguments(old_position.arguments.all())
  284 +
  285 + args_strs = []
  286 + for arg in sorted_arguments:
  287 + args_strs.append(arg.text_rep)
  288 +
  289 + pos_text_rep = '%s{%s}' % (','.join(sorted_categories_strs), ';'.join(args_strs))
  290 +
  291 + try:
  292 + new_position = Position.objects.get(text_rep=pos_text_rep)
  293 + except Position.DoesNotExist:
  294 + new_position = Position(text_rep=pos_text_rep)
  295 + new_position.save()
  296 +
  297 + for category_name in sorted_categories_strs:
  298 + category = PositionCategory.objects.get(category=category_name)
  299 + new_position.categories.add(category)
  300 +
  301 + for arg in old_position.arguments.all():
  302 + new_position.arguments.add(arg)
  303 +
  304 + return new_position
  305 +
  306 + def get_semantic_operations(self, lemma, schemata_conversions):
  307 + operations = []
  308 +
  309 + frames = lemma.entry_obj.visible_frames() # czy actual??
  310 +
  311 + for conv in schemata_conversions:
  312 + schema_operations = self.get_reconnect_operations(frames, conv)
  313 + operations.extend(schema_operations)
  314 +
  315 + return operations
  316 +
  317 +
  318 + def get_reconnect_operations(self, frames, conversion):
  319 + operations = []
  320 +
  321 + for frame in frames:
  322 + for compl in frame.complements.all():
  323 + arg_ref = create_argument_ref(frame, compl)
  324 + for rel in compl.realizations.all():
  325 + schema_change = False
  326 + position_change = None
  327 + if rel.frame.pk == conversion['old_schema'].pk:
  328 + schema_change = True
  329 + for change in conversion['positions_changes']:
  330 + if change['from'].pk == rel.position.pk:
  331 + position_change = change
  332 + if schema_change:
  333 + old_phrase_type_ref = create_phrase_type_ref(rel.frame, rel.position,
  334 + rel.argument, rel.alternation)
  335 + if position_change:
  336 + new_phrase_type_ref = create_phrase_type_ref(conversion['new_schema'],
  337 + position_change['to'],
  338 + rel.argument,
  339 + rel.alternation)
  340 + else:
  341 + new_phrase_type_ref = create_phrase_type_ref(conversion['new_schema'],
  342 + rel.position,
  343 + rel.argument,
  344 + rel.alternation)
  345 + if new_phrase_type_ref != old_phrase_type_ref:
  346 + operations.append(create_operation('disconnect', arg_ref, old_phrase_type_ref))
  347 + operations.append(create_operation('connect', arg_ref, new_phrase_type_ref))
  348 + return operations
  349 +
  350 + def disconnect_example_operation(self, lu, example):
  351 + return {'operation': 'remove_example', 'unit': lu.id, 'example': example.id}
  352 +
  353 + def connect_example_operation(self, lu, example):
  354 + return {'operation': 'add_example', 'unit': lu.id, 'example': example.id}
... ...
dictionary/management/commands/check_text_reps.py 0 → 100644
  1 +# -*- coding:utf-8 -*-
  2 +
  3 +import codecs
  4 +import os
  5 +
  6 +from django.core.management.base import BaseCommand
  7 +from django.db.models import Count
  8 +
  9 +from dictionary.common_func import frame_data_to_text_rep, \
  10 + position_data_to_text_rep
  11 +from dictionary.models import Argument, Argument_Model, Frame, \
  12 + Position, sortatributes, AttributeParameter
  13 +from settings import PROJECT_PATH
  14 +
  15 +WRONG_PARAMETERS_PATH = os.path.join(PROJECT_PATH, 'data', 'wrong', 'wrong_parameters_po_reperacji_20180801.txt')
  16 +WRONG_ARGUMENTS_PATH = os.path.join(PROJECT_PATH, 'data', 'wrong', 'wrong_arguments_po_reperacji_20180801.txt')
  17 +WRONG_POSITIONS_PATH = os.path.join(PROJECT_PATH, 'data', 'wrong', 'wrong_positions_po_reperacji_20180801.txt')
  18 +WRONG_FRAMES_PATH = os.path.join(PROJECT_PATH, 'data', 'wrong', 'wrong_frames_po_reperacji_20180801.txt')
  19 +
  20 +class Command(BaseCommand):
  21 + args = 'none'
  22 + help = 'Looking for wrong text_reps.'
  23 +
  24 + def handle(self, **options):
  25 + check_attr_parameters()
  26 + check_arguments_text_reps()
  27 + check_positions_text_reps()
  28 + check_frames_text_reps()
  29 +
  30 +
  31 +def check_attr_parameters():
  32 + print 'Checking parameters.'
  33 + # try:
  34 + wrong_parameters_file = codecs.open(WRONG_PARAMETERS_PATH, 'wt', 'utf-8')
  35 + for attr_param in AttributeParameter.objects.all():
  36 + print attr_param
  37 + possible_param_objs = AttributeParameter.objects.annotate(subparams_count=Count('subparameters')).filter(
  38 + subparams_count=attr_param.subparameters.count())
  39 + for subparam in attr_param.subparameters.all():
  40 + possible_param_objs = possible_param_objs.filter(subparameters=subparam)
  41 + # possible_param_objs = possible_param_objs.distinct()
  42 + possible_param_objs = possible_param_objs.filter(type=attr_param.type)
  43 + if possible_param_objs.count() > 1:
  44 + wrong_parameters_file.write(u'%s' % unicode(attr_param))
  45 + # finally:
  46 + wrong_parameters_file.close()
  47 +
  48 +
  49 +def check_arguments_text_reps():
  50 + print 'Checking arguments.'
  51 + # try:
  52 + wrong_arguments_file = codecs.open(WRONG_ARGUMENTS_PATH, 'wt', 'utf-8')
  53 + for argument in Argument.objects.all():
  54 + print argument
  55 + proper_text_rep = arg_data_to_text_rep(argument)
  56 + if proper_text_rep != argument.text_rep:
  57 + wrong_arguments_file.write(u'%s --> proper: %s\n' % (argument.text_rep, proper_text_rep))
  58 + # finally:
  59 + wrong_arguments_file.close()
  60 +
  61 +
  62 +def check_positions_text_reps():
  63 + print 'Checking positions.'
  64 + # try:
  65 + wrong_positions_file = codecs.open(WRONG_POSITIONS_PATH, 'wt', 'utf-8')
  66 + for position in Position.objects.all():
  67 + print position
  68 + proper_text_rep = position_data_to_text_rep(position.categories, position.arguments)
  69 + if proper_text_rep != position.text_rep:
  70 + wrong_positions_file.write(u'%s --> proper: %s\n' % (position.text_rep, proper_text_rep))
  71 + # finally:
  72 + wrong_positions_file.close()
  73 +
  74 +
  75 +def check_frames_text_reps():
  76 + print 'Checking frames.'
  77 + # try:
  78 + wrong_frames_file = codecs.open(WRONG_FRAMES_PATH, 'wt', 'utf-8')
  79 + for frame in Frame.objects.all():
  80 + print frame
  81 + proper_text_rep = frame_data_to_text_rep(frame.characteristics, frame.positions)
  82 + if proper_text_rep != frame.text_rep:
  83 + wrong_frames_file.write(u'%s --> proper: %s\n' % (frame.text_rep, proper_text_rep))
  84 + for lemma in frame.lemmas.all():
  85 + if lemma.old:
  86 + wrong_frames_file.write(u'\t\told: %s\n' % (lemma.entry))
  87 + else:
  88 + wrong_frames_file.write('u\t\tnew: %s\n' % (lemma.entry))
  89 + for opinion in frame.opinions.all():
  90 + wrong_frames_file.write(u'\t\topinion: %s\n' % (opinion.frame.text_rep))
  91 + for example in frame.nkjp_examples.all():
  92 + wrong_frames_file.write(u'\t\texample: %s\n' % (example.sentence))
  93 + # finally:
  94 + wrong_frames_file.close()
  95 +
  96 +
  97 +def arg_data_to_text_rep(argument):
  98 + sorted_attributes = sortatributes(argument)
  99 + arg_model = Argument_Model.objects.get(arg_model_name=argument.type)
  100 + # sorted_attributes = sort_arg_attributes(arg_model, attributes)
  101 + attributes_text_reps = [unicode(attr) for attr in sorted_attributes]
  102 + if len(sorted_attributes) == 0:
  103 + arg_text_rep = argument.type
  104 + elif arg_model.hide_type:
  105 + arg_text_rep = u'%s' % (','.join(attributes_text_reps))
  106 + else:
  107 + arg_text_rep = u'%s(%s)' % (argument.type, ','.join(attributes_text_reps))
  108 + return arg_text_rep
... ...
dictionary/models.py
... ... @@ -500,12 +500,13 @@ class NKJP_Example(Model):
500 500 )
501 501  
502 502 def get_or_create_nkjp_example(frame, arguments, sentence, source,
503   - comment, opinion, approvers, approved):
  503 + comment, opinion, approvers, approved, semantic):
504 504 created = False
505 505 example = None
506 506 possible_examples = NKJP_Example.objects.filter(frame=frame, sentence=sentence,
507 507 source=source, comment=comment,
508   - opinion=opinion, approved=approved)
  508 + opinion=opinion, approved=approved,
  509 + semantic=semantic)
509 510 if possible_examples.exists():
510 511 for arg_sel in arguments:
511 512 possible_examples = possible_examples.filter(arguments=arg_sel)
... ... @@ -521,27 +522,31 @@ def get_or_create_nkjp_example(frame, arguments, sentence, source,
521 522 example = create_nkjp_example(frame=frame, arguments=arguments,
522 523 sentence=sentence, source=source,
523 524 comment=comment, opinion=opinion,
524   - approvers=approvers, approved=approved)
  525 + approvers=approvers, approved=approved,
  526 + semantic=semantic)
525 527 created = True
526 528 else:
527 529 example = create_nkjp_example(frame=frame, arguments=arguments,
528 530 sentence=sentence, source=source,
529 531 comment=comment, opinion=opinion,
530   - approvers=approvers, approved=approved)
  532 + approvers=approvers, approved=approved,
  533 + semantic=semantic)
531 534 created = True
532 535 else:
533 536 example = create_nkjp_example(frame=frame, arguments=arguments,
534 537 sentence=sentence, source=source,
535 538 comment=comment, opinion=opinion,
536   - approvers=approvers, approved=approved)
  539 + approvers=approvers, approved=approved,
  540 + semantic=semantic)
537 541 created = True
538 542 return example, created
539 543  
540 544 def create_nkjp_example(frame, arguments, sentence, source,
541   - comment, opinion, approvers, approved):
  545 + comment, opinion, approvers, approved, semantic):
542 546 example = NKJP_Example(frame=frame, sentence=sentence,
543 547 source=source, comment=comment,
544   - opinion=opinion, approved=approved)
  548 + opinion=opinion, approved=approved,
  549 + semantic=semantic)
545 550 example.save()
546 551 example.arguments.add(*arguments)
547 552 example.approvers.add(*approvers)
... ...