Bei den IDs bist Du bei den Namen inkonsistent was Ein- und Mehrzahl angeht. Ich würde Präfixe dort weglassen wenn sie den eigenen Tabellennamen wiederholen, denn dann hat der Präfix keinen Mehrwert. Also in beiden Tabellen einfach nur `id` für den Primärschlüssel.
Gross-/Kleinschreibung würde ich bei SQL-Datenbanken vermeiden. Manche unterscheiden das, manche nicht, bei manchen hängt es von der Konfiguration ab. Kleinbuchstaben mit Unterstrichen funktionieren bei allen gleich.
`times` würde ich `slot` oder `time_slot` nennen damit man nicht für Tabelle, also ein Objekt, den gleichen Namen hat wie für ein Attribut des Objekts. Das führt dann gerne mal zu Verwirrungen ob an den Namen `time` jetzt so ein komplettes Objekt oder nur der Wert des `time`-Attributs gebunden ist.
Generell ist es eine gute Idee die Namen zwischen Programm und Datenbank zu ”synchronisieren”. Wenn das in der Datenbank `job_title` heisst, sollte es im Programm nicht `job` oder `name` heissen, sondern auch `job_title`. Oder eben auch in der Datenbank `name` wenn man es im Programm `name` nennt. Oder nur `title`, denn `job.job_title` ist auch wieder redundant und enthält nicht mehr Informationen wie `job.title`. Bei Fremdschlüsseln macht so ein Präfix vor `_id` Sinn, damit man sieht in welche Tabelle der Schlüssel verweist. Also eher `slot.job_id` als `slot.job_done`. Von dem Namensmuster würde ich erst abweichen wenn es mehr als einen Fremdschlüssel in einer Tabelle auf eine andere gibt.
`time` sollte auch in der Datenbank den entsprechenden Typ haben, also TIMESTAMP oder DATETIME. Und man sollte `sqlite3` dann auch sagen das solche Typen zu Python's `datetime.datetime` abgebildet werden sollen. Ich vergesse immer wieder wie man das macht, weil SQLAlchemy schon automatisch das ”richtige” tut.

Da die Jobtitel/-namen nicht eindeutig sind, geht das mit `map_job_to_id()` und `remove_job()` so nicht. Beim Abfragen nennst Du den Wert jobID obwohl a) bei der Abfrage mehr als eine ID herum kommen kann und b) der Rückgabewert von `cursor.execute()` nicht definiert ist. Du willst da vielleicht vom Cursor das Ergebnis abfragen und ihn auch wieder schliessen bevor die Methode verlassen wird.
Bei anderen Methoden machst Du diesen Fehler auch.
Du verwendest Name oder Zeit als Identifizierung von Datensätzen. Dafür sind aber die IDs da! `remove_job()` löscht *alle* Jobs mit dem gleichen Namen/Titel. Die DB-API 2.0 beschreibt als optionale Erweiterung ein `lastrowid` auf `Cursor`-Objekten das die ID des zuletzt mit `execute()` eingefügten Datensatzes von diesem `Cursor` enthält. `sqlite3` hat das implementiert. Das sollte der Rückgabewert sein wenn man einen Datensatz anlegt. Beziehungsweise sollte das Bestandteil des Rückgabewertes sein wenn man anfängt mit Klassen für die Werte im Programm zu arbeiten. Und mit der Zeit bastelt man sich so sein eigenes ORM. Erwähnte ich SQLAlchemy schon mal?

Bei `get_jobs()` fehlt der Tabellenname in der Abfrage.
Wenn das löschen der Zeiten nicht erwünscht ist wenn man einen kompletten Job löscht, dann sollte auch das löschen von `job`-Einträgen nicht erwünscht sein. Denn die Zeiten verweisen dann auf einen nicht mehr existierenden Eintrag in einer anderen Tabelle. Das löst man üblicherweise mit einem Flag das `job`-Einträge als ”gelöscht” oder nicht mehr ”aktiv” markiert. Eine weitere Möglichkeit wäre ein Datumsfeld welches den Zeitpunkt der Deaktivierung oder NULL enthält wenn das für die Verarbeitung der Daten wichtig ist. Dann kann man sich auch rückwirkend für beliebige Zeitpunkte die aktiven Jobs filtern. Falls Jobs auch ein Anfangsdatum haben, könnte man das natürlich auch vermerken.
Apropos Flags: Hier hat SQLAlchemy den Vorteil einen BOOLEAN-Typ zu unterstützen auch wenn die dahinterliegende Datenbank den Typ nicht kennt.