Anchor-annotator 0.5.0__py3-none-any.whl → 0.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Anchor_annotator
3
- Version: 0.5.0
3
+ Version: 0.7.0
4
4
  Summary: Anchor annotator is a program for inspecting corpora for the Montreal Forced Aligner and correcting transcriptions and pronunciations.
5
5
  Home-page: https://github.com/MontrealCorpusTools/Anchor-annotator
6
6
  Author: Montreal Corpus Tools
@@ -0,0 +1,22 @@
1
+ anchor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ anchor/__main__.py,sha256=5ufG8lcx2x1am-04xI991AG7saJd24dxPw5JzjmB878,45
3
+ anchor/_version.py,sha256=akvr8ObxvMF-aaLBzW41juT4_KL3BjQUrjbwkIuQXMk,411
4
+ anchor/command_line.py,sha256=EucG805HyWk_zkMO9RXv9Yj0I0JVdDLZb1_DX2_ISjM,503
5
+ anchor/db.py,sha256=LlZzAy4bjmJIu0v4ev5Qjg_Fh2n9sMsKI2nAY1pwd0A,5057
6
+ anchor/main.py,sha256=Lyr3ppr-nzxaU7ZmWXc-luMsOtRBbV4ebCzk3rygur4,127781
7
+ anchor/models.py,sha256=35l7Kw3LVy-_ozdV_0ApSkKyCPViBwBmAukoq-jw90o,97668
8
+ anchor/plot.py,sha256=imNRLI76VgEf4n9UGNvIaTsqn65hqnN396e4iwRTh70,113387
9
+ anchor/resources_rc.py,sha256=tzJHrJw3MpjAlnj-DtCmaR4A8gAaLF966XEXs5HNIjc,8464375
10
+ anchor/settings.py,sha256=N2gRFQEpY4pLYgcDz1Aq-2c7CfmbNxmRmVcPijrHsCo,52118
11
+ anchor/ui_corpus_manager.py,sha256=e3ybOd4UdYarrLBATxI8vIFnioa4R_BHrbsEz5mJ5eA,8564
12
+ anchor/ui_error_dialog.py,sha256=HKbjGT_jtdb9jfn9THQMbl1fmcdWyjYDazM4hCwZ5Yo,3931
13
+ anchor/ui_main_window.py,sha256=XK91lhFAIEURZ6nwxIA74X-8j-P76JuJsN-ahun65rw,37043
14
+ anchor/ui_preferences.py,sha256=g3tcjAMFKIAqUJNEke7ww4LkdeTFA1zb8_lrhF6k5fo,43271
15
+ anchor/undo.py,sha256=T8CJpSZVZbItpU7KMZU2F49mNv1wo0rvMWtNIEbieeo,32856
16
+ anchor/widgets.py,sha256=NjQAc02QVu97QClhXcylj_P6IP0DsxWae_eiZR5Bw3M,159300
17
+ anchor/workers.py,sha256=ciVOlK15MiDq7juAivcQB6PEiEs7DemP0BOrcpnm2to,182624
18
+ Anchor_annotator-0.7.0.dist-info/LICENSE,sha256=C0oIsblENEgWQ7XMNdYoXyXsIA5wa3YF0I9lK3H7A1s,1076
19
+ Anchor_annotator-0.7.0.dist-info/METADATA,sha256=hvYb1JLmhGJEfwyTNGckZl6tqtj407fmYYdPqPOgwcE,1500
20
+ Anchor_annotator-0.7.0.dist-info/WHEEL,sha256=FZ75kcLy9M91ncbIgG8dnpCncbiKXSRGJ_PFILs6SFg,91
21
+ Anchor_annotator-0.7.0.dist-info/top_level.txt,sha256=wX6ZKxImGRZKFQjs3f6XYw_TfbAp6Xs3SmbLfLbFAJ0,7
22
+ Anchor_annotator-0.7.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (71.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
anchor/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.5.0'
16
- __version_tuple__ = version_tuple = (0, 5, 0)
15
+ __version__ = version = '0.7.0'
16
+ __version_tuple__ = version_tuple = (0, 7, 0)
anchor/db.py CHANGED
@@ -105,6 +105,7 @@ class AnchorCorpus(AnchorSqlBase):
105
105
  custom_mapping_path = Column(PathType, nullable=True)
106
106
  reference_directory = Column(PathType, nullable=True)
107
107
  current = Column(Boolean, nullable=False, default=False, index=True)
108
+ # last_used = Column(DateTime, nullable=False, server_default=sqlalchemy.func.now(), index=True)
108
109
 
109
110
  acoustic_model_id = Column(Integer, ForeignKey("acoustic_model.id"), index=True, nullable=True)
110
111
  acoustic_model = relationship("AcousticModel", back_populates="corpora")
anchor/main.py CHANGED
@@ -123,6 +123,7 @@ class MainWindow(QtWidgets.QMainWindow):
123
123
  self.media_player = MediaPlayer(self)
124
124
  self.media_player.playbackStateChanged.connect(self.handleAudioState)
125
125
  self.media_player.audioReady.connect(self.file_loaded)
126
+ self.media_player.playingChanged.connect(self.update_play_button)
126
127
  self.media_player.timeChanged.connect(
127
128
  self.ui.utteranceDetailWidget.plot_widget.audio_plot.update_play_line
128
129
  )
@@ -345,14 +346,11 @@ class MainWindow(QtWidgets.QMainWindow):
345
346
  session.commit()
346
347
 
347
348
  def file_loaded(self, ready):
348
- if ready:
349
- self.ui.playAct.setEnabled(ready)
350
- self.ui.muteAct.setEnabled(ready)
351
- else:
352
- self.ui.playAct.setEnabled(False)
353
- self.ui.playAct.setChecked(False)
354
- self.ui.muteAct.setChecked(False)
355
- self.ui.muteAct.setEnabled(False)
349
+ self.ui.playAct.setEnabled(ready)
350
+ self.ui.muteAct.setEnabled(ready)
351
+
352
+ def update_play_button(self, playing):
353
+ self.ui.playAct.setChecked(playing)
356
354
 
357
355
  def corpus_changed(self, clean):
358
356
  if clean:
@@ -406,9 +404,15 @@ class MainWindow(QtWidgets.QMainWindow):
406
404
  elif function == "Diarizing utterances":
407
405
  worker = workers.SpeakerDiarizationWorker(self.corpus_model.session, **extra_args[0])
408
406
  worker.signals.result.connect(finished_function)
409
- elif function == "Counting diarization results":
407
+ elif function == "Diarizing speakers":
408
+ worker = workers.SpeakerUtterancesWorker(self.corpus_model.session, **extra_args[0])
409
+ worker.signals.result.connect(finished_function)
410
+ elif function == "Counting utterance diarization results":
410
411
  worker = workers.SpeakerDiarizationWorker(self.corpus_model.session, **extra_args[0])
411
412
  worker.signals.result.connect(finished_function)
413
+ elif function == "Counting speaker diarization results":
414
+ worker = workers.SpeakerUtterancesWorker(self.corpus_model.session, **extra_args[0])
415
+ worker.signals.result.connect(finished_function)
412
416
  elif function == "Merging speakers":
413
417
  self.set_application_state("loading")
414
418
  worker = workers.MergeSpeakersWorker(self.corpus_model.session, **extra_args[0])
@@ -720,7 +724,6 @@ class MainWindow(QtWidgets.QMainWindow):
720
724
  def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
721
725
  for worker in self.workers:
722
726
  worker.stopped.set()
723
- self.file_selection_model.clean_up_for_close()
724
727
  self.file_utterances_model.clean_up_for_close()
725
728
  self.settings.setValue(
726
729
  AnchorSettings.UTTERANCES_VISIBLE, self.ui.utteranceDockWidget.isVisible()
@@ -765,6 +768,8 @@ class MainWindow(QtWidgets.QMainWindow):
765
768
  return
766
769
  if self.thread_pool.activeThreadCount() > 0:
767
770
  return
771
+ if self.file_selection_model.thread_pool.activeThreadCount() > 0:
772
+ return
768
773
  if self.corpus_model.session is not None:
769
774
  self.corpus_model.session = None
770
775
  self.corpus_model.corpus.cleanup_connections()
@@ -851,8 +856,10 @@ class MainWindow(QtWidgets.QMainWindow):
851
856
  self.ui.transcriptionWidget.button.setDefaultAction(self.ui.transcribeCorpusAct)
852
857
  self.ui.utteranceListWidget.oov_button.setDefaultAction(self.ui.oovsOnlyAct)
853
858
  self.ui.alignmentWidget.button.setDefaultAction(self.ui.alignCorpusAct)
859
+ self.ui.alignmentWidget.verify_button.setDefaultAction(self.ui.verifyTranscriptsAct)
854
860
 
855
861
  self.ui.alignCorpusAct.triggered.connect(self.begin_alignment)
862
+ self.ui.verifyTranscriptsAct.triggered.connect(self.begin_verify_transcripts)
856
863
  self.ui.diarizationWidget.refresh_ivectors_action.triggered.connect(
857
864
  self.begin_refresh_ivectors
858
865
  )
@@ -1324,6 +1331,9 @@ class MainWindow(QtWidgets.QMainWindow):
1324
1331
  self.corpus_model.has_reference_alignments = True
1325
1332
  elif w.workflow_type is WorkflowType.transcription:
1326
1333
  self.corpus_model.has_transcribed_alignments = True
1334
+ elif w.workflow_type is WorkflowType.transcript_verification:
1335
+ self.corpus_model.has_transcribed_alignments = True
1336
+ self.corpus_model.has_transcript_verification_alignments = True
1327
1337
  elif w.workflow_type is WorkflowType.per_speaker_transcription:
1328
1338
  self.corpus_model.has_per_speaker_transcribed_alignments = True
1329
1339
 
@@ -1407,6 +1417,15 @@ class MainWindow(QtWidgets.QMainWindow):
1407
1417
  self.set_application_state("loading")
1408
1418
  self.ui.loadingScreen.setCorpusName("Performing alignment...")
1409
1419
 
1420
+ def begin_verify_transcripts(self):
1421
+ self.enableMfaActions(False)
1422
+ self.alignment_worker.set_params(
1423
+ self.corpus_model.corpus, self.acoustic_model, self.ui.alignmentWidget.parameters()
1424
+ )
1425
+ self.alignment_worker.start()
1426
+ self.set_application_state("loading")
1427
+ self.ui.loadingScreen.setCorpusName("Verifying transcriptions...")
1428
+
1410
1429
  def begin_refresh_ivectors(self):
1411
1430
  self.enableMfaActions(False)
1412
1431
  self.compute_ivectors_worker.set_params(self.corpus_model, reset=False)
@@ -1560,6 +1579,7 @@ class MainWindow(QtWidgets.QMainWindow):
1560
1579
  )
1561
1580
  self.corpus_model.update_data()
1562
1581
  self.check_actions()
1582
+ self.corpus_model.update_latest_alignment_workflow()
1563
1583
  self.set_application_state("loaded")
1564
1584
 
1565
1585
  def finalize_utterance_alignment(self, utterance_id: int):
@@ -2532,6 +2552,7 @@ class OptionsDialog(QtWidgets.QDialog):
2532
2552
  self.ui.tabWidget.setCurrentIndex(0)
2533
2553
  # self.setFont(self.settings.font)
2534
2554
  # self.setStyleSheet(self.settings.style_sheet)
2555
+ self.update_preset_theme()
2535
2556
 
2536
2557
  def set_theme(self, theme):
2537
2558
  self.ui.primaryBaseEdit.set_color(theme[self.settings.PRIMARY_BASE_COLOR])
anchor/models.py CHANGED
@@ -22,8 +22,8 @@ from montreal_forced_aligner.corpus.acoustic_corpus import (
22
22
  AcousticCorpus,
23
23
  AcousticCorpusWithPronunciations,
24
24
  )
25
- from montreal_forced_aligner.data import PhoneType, WordType
26
- from montreal_forced_aligner.db import File, Phone, Speaker, Utterance
25
+ from montreal_forced_aligner.data import PhoneType, WordType, WorkflowType
26
+ from montreal_forced_aligner.db import CorpusWorkflow, File, Phone, Speaker, Utterance, Word
27
27
  from montreal_forced_aligner.dictionary.mixins import (
28
28
  DEFAULT_CLITIC_MARKERS,
29
29
  DEFAULT_COMPOUND_MARKERS,
@@ -97,14 +97,13 @@ class TextFilterQuery:
97
97
  if not text.endswith(word_break_set):
98
98
  text += word_break_set
99
99
  if posix:
100
- text = text.replace(r"\b", word_break_set)
100
+ text = text.replace(r"\b", r"\y")
101
101
  if text.startswith(r"\b"):
102
102
  text = rf"((?<={WORD_BREAK_REGEX_SET})|(?<=^))" + text[2:]
103
103
  if text.endswith(r"\b"):
104
104
  text = text[:-2] + rf"((?={WORD_BREAK_REGEX_SET})|(?=$))"
105
- if self.regex or self.word:
106
- if not self.case_sensitive:
107
- text = "(?i)" + text
105
+ if not self.case_sensitive:
106
+ text = "(?i)" + text
108
107
  return text
109
108
 
110
109
 
@@ -202,10 +201,10 @@ class FileUtterancesModel(QtCore.QAbstractListModel):
202
201
  self.reversed_indices = {}
203
202
  self.speaker_channel_mapping = {}
204
203
  self.corpus_model: CorpusModel = None
205
- self.waveform_worker = workers.WaveformWorker()
206
- self.speaker_tier_worker = workers.SpeakerTierWorker()
207
- self.speaker_tier_worker.signals.result.connect(self.finalize_loading_utterances)
208
- self.waveform_worker.signals.result.connect(self.finalize_loading_wave_form)
204
+ self.closing = False
205
+
206
+ self.thread_pool = QtCore.QThreadPool()
207
+ self.thread_pool.setMaxThreadCount(4)
209
208
 
210
209
  def get_utterance(self, utterance_id: int) -> Utterance:
211
210
  try:
@@ -217,8 +216,7 @@ class FileUtterancesModel(QtCore.QAbstractListModel):
217
216
  self.corpus_model = corpus_model
218
217
 
219
218
  def clean_up_for_close(self):
220
- self.waveform_worker.stop()
221
- self.speaker_tier_worker.stop()
219
+ self.closing = True
222
220
 
223
221
  def set_file(self, file_id):
224
222
  self.file = (
@@ -226,11 +224,13 @@ class FileUtterancesModel(QtCore.QAbstractListModel):
226
224
  )
227
225
  self.y = None
228
226
  self.get_utterances()
229
- self.waveform_worker.stop()
230
- self.waveform_worker.set_params(self.file.sound_file.sound_file_path)
231
- self.waveform_worker.start()
227
+ waveform_worker = workers.WaveformWorker(self.file.sound_file.sound_file_path)
228
+ waveform_worker.signals.result.connect(self.finalize_loading_wave_form)
229
+ self.thread_pool.start(waveform_worker)
232
230
 
233
231
  def finalize_loading_utterances(self, results):
232
+ if self.closing:
233
+ return
234
234
  utterances, file_id = results
235
235
  if file_id != self.file.id:
236
236
  return
@@ -246,6 +246,8 @@ class FileUtterancesModel(QtCore.QAbstractListModel):
246
246
  self.utterancesReady.emit()
247
247
 
248
248
  def finalize_loading_wave_form(self, results):
249
+ if self.closing:
250
+ return
249
251
  y, file_path = results
250
252
  if self.file is None or file_path != self.file.sound_file.sound_file_path:
251
253
  return
@@ -264,15 +266,17 @@ class FileUtterancesModel(QtCore.QAbstractListModel):
264
266
  self.endRemoveRows()
265
267
  if self.file is None:
266
268
  return
267
- self.speaker_tier_worker.stop()
268
- self.speaker_tier_worker.query_alignment = (
269
- self.corpus_model.has_alignments
270
- or self.corpus_model.has_reference_alignments
271
- or self.corpus_model.has_transcribed_alignments
269
+ speaker_tier_worker = workers.SpeakerTierWorker(
270
+ self.corpus_model.session,
271
+ self.file.id,
272
+ query_alignment=(
273
+ self.corpus_model.has_alignments
274
+ or self.corpus_model.has_reference_alignments
275
+ or self.corpus_model.has_transcribed_alignments
276
+ ),
272
277
  )
273
- self.speaker_tier_worker.session = self.corpus_model.session
274
- self.speaker_tier_worker.set_params(self.file.id)
275
- self.speaker_tier_worker.start()
278
+ speaker_tier_worker.signals.result.connect(self.finalize_loading_utterances)
279
+ self.thread_pool.start(speaker_tier_worker)
276
280
 
277
281
  def create_utterance(self, speaker_id: Optional[int], begin: float, end: float):
278
282
  if not self.corpus_model.editable:
@@ -602,18 +606,17 @@ class FileSelectionModel(QtCore.QItemSelectionModel):
602
606
  self.waveform_x = None
603
607
  self.waveform_y = None
604
608
  self.requested_utterance_id = None
605
- self.auto_waveform_worker = workers.AutoWaveformWorker()
606
- self.spectrogram_worker = workers.SpectrogramWorker()
607
- self.pitch_track_worker = workers.PitchWorker()
608
- self.auto_waveform_worker.signals.result.connect(self.finalize_loading_auto_wave_form)
609
- self.spectrogram_worker.signals.result.connect(self.finalize_loading_spectrogram)
610
- self.pitch_track_worker.signals.result.connect(self.finalize_loading_pitch_track)
609
+ self.closing = False
610
+
611
+ self.thread_pool = QtCore.QThreadPool()
612
+ self.thread_pool.setMaxThreadCount(self.settings.value(self.settings.PLOT_THREAD_COUNT))
611
613
  self.model().waveformReady.connect(self.load_audio_selection)
612
614
  self.model().utterancesReady.connect(self.finalize_set_new_file)
613
615
  self.viewChanged.connect(self.load_audio_selection)
614
616
  self.model().selectionRequested.connect(self.update_selected_utterances)
615
617
  self.view_change_timer = QtCore.QTimer()
616
- self.view_change_timer.setInterval(50)
618
+ self.view_change_timer.setSingleShot(True)
619
+ self.view_change_timer.setInterval(10)
617
620
  self.view_change_timer.timeout.connect(self.send_selection_update)
618
621
 
619
622
  def selected_utterances(self):
@@ -633,18 +636,17 @@ class FileSelectionModel(QtCore.QItemSelectionModel):
633
636
  y = self.model().y[begin_samp:end_samp, self.selected_channel]
634
637
  else:
635
638
  y = self.model().y[begin_samp:end_samp]
636
- self.spectrogram_worker.stop()
637
- self.spectrogram_worker.set_params(
639
+ spectrogram_worker = workers.SpectrogramWorker(
638
640
  y,
639
641
  self.model().file.sound_file.sample_rate,
640
642
  self.min_time,
641
643
  self.max_time,
642
644
  self.selected_channel,
643
645
  )
644
- self.spectrogram_worker.start()
646
+ spectrogram_worker.signals.result.connect(self.finalize_loading_spectrogram)
647
+ self.thread_pool.start(spectrogram_worker)
645
648
 
646
- self.pitch_track_worker.stop()
647
- self.pitch_track_worker.set_params(
649
+ pitch_track_worker = workers.PitchWorker(
648
650
  y,
649
651
  self.model().file.sound_file.sample_rate,
650
652
  self.min_time,
@@ -653,10 +655,10 @@ class FileSelectionModel(QtCore.QItemSelectionModel):
653
655
  self.bottom_point,
654
656
  self.separator_point,
655
657
  )
656
- self.pitch_track_worker.start()
658
+ pitch_track_worker.signals.result.connect(self.finalize_loading_pitch_track)
659
+ self.thread_pool.start(pitch_track_worker)
657
660
 
658
- self.auto_waveform_worker.stop()
659
- self.auto_waveform_worker.set_params(
661
+ auto_waveform_worker = workers.AutoWaveformWorker(
660
662
  y,
661
663
  self.separator_point,
662
664
  self.top_point,
@@ -664,12 +666,11 @@ class FileSelectionModel(QtCore.QItemSelectionModel):
664
666
  self.max_time,
665
667
  self.selected_channel,
666
668
  )
667
- self.auto_waveform_worker.start()
669
+ auto_waveform_worker.signals.result.connect(self.finalize_loading_auto_wave_form)
670
+ self.thread_pool.start(auto_waveform_worker)
668
671
 
669
672
  def clean_up_for_close(self):
670
- self.spectrogram_worker.stop()
671
- self.pitch_track_worker.stop()
672
- self.auto_waveform_worker.stop()
673
+ self.closing = True
673
674
 
674
675
  @property
675
676
  def plot_min(self):
@@ -684,6 +685,8 @@ class FileSelectionModel(QtCore.QItemSelectionModel):
684
685
  return self.max_time
685
686
 
686
687
  def finalize_loading_spectrogram(self, results):
688
+ if self.closing:
689
+ return
687
690
  if results is None:
688
691
  self.spectrogram = None
689
692
  self.min_db = None
@@ -691,28 +694,28 @@ class FileSelectionModel(QtCore.QItemSelectionModel):
691
694
  self.spectrogramReady.emit()
692
695
  return
693
696
  stft, channel, begin, end, min_db, max_db = results
697
+ if begin != self.min_time or end != self.max_time:
698
+ return
694
699
  if self.settings.right_to_left:
695
700
  stft = np.flip(stft, 1)
696
- begin, end = -end, -begin
697
- if begin != self.plot_min or end != self.plot_max:
698
- return
699
701
  self.spectrogram = stft
700
702
  self.min_db = self.min_db
701
703
  self.max_db = self.max_db
702
704
  self.spectrogramReady.emit()
703
705
 
704
706
  def finalize_loading_pitch_track(self, results):
707
+ if self.closing:
708
+ return
705
709
  if results is None:
706
710
  self.pitch_track_y = None
707
711
  self.pitch_track_x = None
708
712
  self.pitchTrackReady.emit()
709
713
  return
710
714
  pitch_track, voicing_track, channel, begin, end, min_f0, max_f0 = results
715
+ if begin != self.min_time or end != self.max_time:
716
+ return
711
717
  if self.settings.right_to_left:
712
718
  pitch_track = np.flip(pitch_track, 0)
713
- begin, end = -end, -begin
714
- if begin != self.plot_min or end != self.plot_max:
715
- return
716
719
  self.pitch_track_y = pitch_track
717
720
  if pitch_track is None:
718
721
  return
@@ -725,23 +728,24 @@ class FileSelectionModel(QtCore.QItemSelectionModel):
725
728
  self.pitchTrackReady.emit()
726
729
 
727
730
  def finalize_loading_auto_wave_form(self, results):
731
+ if self.closing:
732
+ return
728
733
  y, begin, end, channel = results
734
+ if begin != self.min_time or end != self.max_time:
735
+ return
729
736
  if self.settings.right_to_left:
730
737
  y = np.flip(y, 0)
731
- begin, end = -end, -begin
732
- if begin != self.plot_min or end != self.plot_max:
733
- return
734
738
  x = np.linspace(start=self.plot_min, stop=self.plot_max, num=y.shape[0])
735
739
  self.waveform_x = x
736
740
  self.waveform_y = y
737
741
  self.waveformReady.emit()
738
742
 
739
743
  def select_audio(self, begin, end):
740
- if end is not None and end - begin < 0.05:
744
+ if end is not None and end - begin < 0.025:
741
745
  end = None
742
746
  self.selected_min_time = begin
743
747
  self.selected_max_time = end
744
- if self.selected_min_time != self.min_time:
748
+ if self.selected_min_time != self.min_time or end is not None:
745
749
  self.selectionAudioChanged.emit(False)
746
750
 
747
751
  def request_start_time(self, start_time, update=False):
@@ -881,7 +885,6 @@ class FileSelectionModel(QtCore.QItemSelectionModel):
881
885
  self.view_change_timer.start()
882
886
 
883
887
  def send_selection_update(self):
884
- self.view_change_timer.stop()
885
888
  self.viewChanged.emit(self.min_time, self.max_time)
886
889
 
887
890
  def set_current_file(self, file_id, begin, end, utterance_id, speaker_id, force_update=False):
@@ -1916,14 +1919,30 @@ class DiarizationModel(TableModel):
1916
1919
  self.runFunction.emit("Reassigning utterances for speaker", self.update_data, [kwargs])
1917
1920
 
1918
1921
  def update_result_count(self):
1919
- self.runFunction.emit(
1920
- "Counting diarization results", self.finalize_result_count, [self.count_kwargs]
1921
- )
1922
+ if self.in_speakers:
1923
+ self.runFunction.emit(
1924
+ "Counting speaker diarization results",
1925
+ self.finalize_result_count,
1926
+ [self.count_kwargs],
1927
+ )
1928
+ else:
1929
+ self.runFunction.emit(
1930
+ "Counting utterance diarization results",
1931
+ self.finalize_result_count,
1932
+ [self.count_kwargs],
1933
+ )
1922
1934
 
1923
1935
  def update_data(self):
1924
1936
  if not self.corpus_model.corpus.has_any_ivectors():
1925
1937
  return
1926
- self.runFunction.emit("Diarizing utterances", self.finish_update_data, [self.query_kwargs])
1938
+ if self.in_speakers:
1939
+ self.runFunction.emit(
1940
+ "Diarizing speakers", self.finish_update_data, [self.query_kwargs]
1941
+ )
1942
+ else:
1943
+ self.runFunction.emit(
1944
+ "Diarizing utterances", self.finish_update_data, [self.query_kwargs]
1945
+ )
1927
1946
 
1928
1947
 
1929
1948
  class CorpusModel(TableModel):
@@ -2040,6 +2059,31 @@ class CorpusModel(TableModel):
2040
2059
  self.has_reference_alignments = False
2041
2060
  self.has_transcribed_alignments = False
2042
2061
  self.has_per_speaker_transcribed_alignments = False
2062
+ self.has_transcript_verification_alignments = False
2063
+ self.latest_alignment_workflow = None
2064
+
2065
+ def update_latest_alignment_workflow(self):
2066
+ with self.corpus.session() as session:
2067
+ query = (
2068
+ session.query(CorpusWorkflow.id)
2069
+ .filter(CorpusWorkflow.workflow_type.in_(WorkflowType.alignment_workflows()))
2070
+ .order_by(sqlalchemy.desc(CorpusWorkflow.time_stamp))
2071
+ .limit(1)
2072
+ ).first()
2073
+ if query is not None:
2074
+ query = query[0]
2075
+ self.latest_alignment_workflow = query
2076
+
2077
+ def get_interjection_count(self):
2078
+ if self.corpus is None:
2079
+ return 0
2080
+ with self.corpus.session() as session:
2081
+ count = (
2082
+ session.query(sqlalchemy.func.count(Word.id))
2083
+ .filter(Word.word_type == WordType.interjection)
2084
+ .first()[0]
2085
+ )
2086
+ return count
2043
2087
 
2044
2088
  def get_speaker_name(self, speaker_id: int):
2045
2089
  if speaker_id not in self.speaker_id_mapping:
@@ -2326,6 +2370,7 @@ class CorpusModel(TableModel):
2326
2370
  self.refresh_files()
2327
2371
  self.refresh_speakers()
2328
2372
  self.refresh_utterances()
2373
+ self.update_latest_alignment_workflow()
2329
2374
 
2330
2375
  def search(
2331
2376
  self,