Skip to content

Commit 116e19f

Browse files
Fix missed and false matches in the Skip Already Transcribed database check (#1684)
Co-authored-by: Raivis Dejus <raivisd@scandiweb.com>
1 parent 794d330 commit 116e19f

2 files changed

Lines changed: 112 additions & 2 deletions

File tree

‎buzz/db/dao/transcription_dao.py‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,15 +334,26 @@ def update_transcription_as_skipped(self, id: UUID):
334334
raise Exception(query.lastError().text())
335335

336336
def find_completed_transcription_by_filename(self, filename: str):
337+
# Match the name literally, "%" and "_" are LIKE wildcards. Paths joined
338+
# with os.path.join on Windows have a backslash before the name.
339+
# Leave out URL imports, they are looked up by URL. Their file is the
340+
# download, always saved as "audio.wav", so it would match unrelated
341+
# files. copy_transcription stores "" rather than NULL in url.
342+
escaped = filename.replace("!", "!!").replace("%", "!%").replace("_", "!_")
337343
query = self._create_query()
338344
query.prepare(
339345
"""
340346
SELECT id FROM transcription
341-
WHERE (file LIKE :pattern OR file = :exact) AND status = 'completed'
347+
WHERE (
348+
file LIKE :slash_pattern ESCAPE '!'
349+
OR file LIKE :backslash_pattern ESCAPE '!'
350+
OR file = :exact
351+
) AND status = 'completed' AND COALESCE(url, '') = ''
342352
ORDER BY time_ended DESC LIMIT 1
343353
"""
344354
)
345-
query.bindValue(":pattern", f"%/{filename}")
355+
query.bindValue(":slash_pattern", f"%/{escaped}")
356+
query.bindValue(":backslash_pattern", f"%\\{escaped}")
346357
query.bindValue(":exact", filename)
347358
if not query.exec():
348359
raise Exception(query.lastError().text())

‎tests/db/dao/transcription_dao_test.py‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,102 @@ def test_find_completed_transcription_by_url(self, transcription_dao):
230230
)
231231
is None
232232
)
233+
234+
@pytest.mark.parametrize(
235+
"name", ["audio.mp3", "report_1.mp3", "100%.mp3", "wow!.mp3"]
236+
)
237+
def test_find_completed_transcription_by_filename(self, transcription_dao, name):
238+
queued = Transcription(
239+
id=str(uuid4()), file=f"/music/in/{name}", status="queued"
240+
)
241+
completed = Transcription(
242+
id=str(uuid4()), file=f"/music/out/{name}", status="completed"
243+
)
244+
other = Transcription(
245+
id=str(uuid4()), file="/music/out/other.mp3", status="completed"
246+
)
247+
for transcription in (queued, completed, other):
248+
transcription_dao.insert(transcription)
249+
250+
assert (
251+
transcription_dao.find_completed_transcription_by_filename(name)
252+
== completed.id
253+
)
254+
assert (
255+
transcription_dao.find_completed_transcription_by_filename("missing.mp3")
256+
is None
257+
)
258+
259+
# Folder watch and Import Folder join paths with os.path.join, which puts a
260+
# backslash before the file name on Windows. CLI paths are stored as typed.
261+
@pytest.mark.parametrize(
262+
"file", [r"C:\Users\me\Music\audio.mp3", r"C:/Users/me/Music\audio.mp3"]
263+
)
264+
def test_find_completed_transcription_by_filename_in_windows_path(
265+
self, transcription_dao, file
266+
):
267+
completed = Transcription(id=str(uuid4()), file=file, status="completed")
268+
transcription_dao.insert(completed)
269+
270+
assert (
271+
transcription_dao.find_completed_transcription_by_filename("audio.mp3")
272+
== completed.id
273+
)
274+
275+
# "_" and "%" are wildcards in LIKE patterns
276+
@pytest.mark.parametrize(
277+
"file, name",
278+
[
279+
("/music/report 1.mp3", "report_1.mp3"),
280+
("/music/Interview 100 - final cut.mp3", "Interview 100%.mp3"),
281+
],
282+
)
283+
def test_find_completed_transcription_by_filename_matches_name_literally(
284+
self, transcription_dao, file, name
285+
):
286+
transcription_dao.insert(
287+
Transcription(id=str(uuid4()), file=file, status="completed")
288+
)
289+
290+
assert transcription_dao.find_completed_transcription_by_filename(name) is None
291+
292+
# A URL import stores its download, a temp file always named audio.wav,
293+
# and is looked up by URL
294+
@pytest.mark.parametrize(
295+
"file",
296+
[
297+
r"C:\Users\me\AppData\Local\Temp\tmpab12cd\audio.wav",
298+
"/tmp/tmpab12cd/audio.wav",
299+
],
300+
)
301+
def test_find_completed_transcription_by_filename_ignores_url_imports(
302+
self, transcription_dao, file
303+
):
304+
transcription_dao.insert(
305+
Transcription(
306+
id=str(uuid4()),
307+
file=file,
308+
url="https://www.youtube.com/watch?v=abc123",
309+
status="completed",
310+
)
311+
)
312+
313+
assert (
314+
transcription_dao.find_completed_transcription_by_filename("audio.wav")
315+
is None
316+
)
317+
318+
# copy_transcription stores "" rather than NULL in url
319+
def test_find_completed_transcription_by_filename_finds_copies(
320+
self, transcription_dao
321+
):
322+
original = Transcription(
323+
id=str(uuid4()), file="/music/audio.wav", status="completed"
324+
)
325+
transcription_dao.insert(original)
326+
copy_id = transcription_dao.copy_transcription(original.id_as_uuid)
327+
transcription_dao.update_transcription_as_completed(copy_id)
328+
329+
assert transcription_dao.find_completed_transcription_by_filename(
330+
"audio.wav"
331+
) == str(copy_id)

0 commit comments

Comments
 (0)