diff --git a/mo/db.py b/mo/db.py
index 7d0ef659a0ef471a1b8ac5ab7e4f4ed95350c7fc..afa1ccca8fb73e3f5f870c4c94e23089fdb1e852 100644
--- a/mo/db.py
+++ b/mo/db.py
@@ -5,6 +5,7 @@ import datetime
import decimal
from enum import Enum as PythonEnum, auto
import locale
+import os
import re
from sqlalchemy import \
Boolean, Column, DateTime, ForeignKey, Integer, String, Text, UniqueConstraint, \
@@ -22,6 +23,7 @@ from sqlalchemy.sql.sqltypes import Numeric
from typing import Optional, List, Tuple
import mo
+import mo.config as config
from mo.place_level import place_levels, PlaceLevel
from mo.util_format import timeformat_short, timedelta, time_and_timedelta
@@ -728,6 +730,14 @@ class Job(Base):
user = relationship('User')
+ def dir_path(self) -> str:
+ """Adresář se soubory příslušejícími k jobu."""
+ # Nepoužíváme mo.util.data_dir, abychom se vyhnuli cyklické závislosti modulů.
+ return os.path.join(config.DATA_DIR, 'jobs', str(self.job_id))
+
+ def file_path(self, name: str) -> str:
+ return os.path.join(self.dir_path(), name)
+
class Message(Base):
__tablename__ = 'messages'
diff --git a/mo/jobs/__init__.py b/mo/jobs/__init__.py
index 31560882195ddd2f5bc8d4ddabb0970a47c1d983..d37becbc0bf9487f8c6549715059ff1d7bfb6640 100644
--- a/mo/jobs/__init__.py
+++ b/mo/jobs/__init__.py
@@ -2,6 +2,7 @@
from datetime import timedelta
import os
+import shutil
from sqlalchemy import or_
from typing import Optional, Dict, Callable, List
@@ -19,20 +20,6 @@ def send_notify():
logger.debug('Job: Není komu poslat notifikaci')
-def job_file_path(name: str) -> str:
- return os.path.join(mo.util.data_dir('jobs'), name)
-
-
-def job_file_size(name: Optional[str]) -> Optional[int]:
- if name is None:
- return None
-
- try:
- return os.path.getsize(job_file_path(name))
- except OSError:
- return -1
-
-
class TheJob:
"""Job z pohledu Pythonu."""
@@ -52,35 +39,45 @@ class TheJob:
return self.job
def create(self, type: db.JobType, for_user: db.User) -> db.Job:
- self.job = db.Job(type=type, state=db.JobState.ready, user=for_user)
- return self.job
-
- def attach_file(self, tmp_name: str, suffix: str):
- """Vytvoří hardlink na daný pracovní soubor v adresáři jobů."""
-
- full_name = mo.util.link_to_dir(tmp_name, mo.util.data_dir('jobs'), suffix=suffix)
- name = os.path.basename(full_name)
- logger.debug(f'Job: Příloha {tmp_name} -> {name}')
- return name
+ self.job = db.Job(type=type, state=db.JobState.preparing, user=for_user)
- def submit(self):
+ # Do DB přidáváme nehotový job, protože potřebujeme znát job_id pro založení adresáře
sess = db.get_session()
sess.add(self.job)
sess.flush()
self.job_id = self.job.job_id
+
logger.info(f'Job: Vytvořen job #{self.job_id} pro uživatele #{self.job.user_id}')
- sess.commit()
+
+ job_dir = self.job.dir_path()
+ if os.path.exists(job_dir):
+ # Hypoteticky by se mohlo stát, že se recykluje job_id od jobu, jehož
+ # vytvoření selhalo před commitem. Zkusíme tedy smazat prázdný adresář.
+ os.rmdir(job_dir)
+ os.mkdir(job_dir)
+
+ return self.job
+
+ def attach_file(self, tmp_name: str, attachment_name: str) -> str:
+ """Vytvoří hardlink na daný pracovní soubor v adresáři jobu."""
+
+ full_name = self.job.file_path(attachment_name)
+ os.link(tmp_name, full_name)
+ logger.debug(f'Job: Příloha {tmp_name} -> {full_name}')
+ return attachment_name
+
+ def submit(self):
+ self.job.state = db.JobState.ready
+ db.get_session().commit()
send_notify()
def _finish_remove(self):
sess = db.get_session()
job = self.job
- if job.in_file is not None:
- mo.util.unlink_if_exists(job_file_path(job.in_file))
-
- if job.out_file is not None:
- mo.util.unlink_if_exists(job_file_path(job.out_file))
+ job_dir = self.job.dir_path()
+ if os.path.exists(job_dir):
+ shutil.rmtree(job_dir)
sess.delete(job)
sess.commit()
diff --git a/mo/web/org_jobs.py b/mo/web/org_jobs.py
index 2fedcf41e79a6616601b5cd95caf96ff5d240519..8cf9786e86bdc9f7d335dd69da3cec78ddd83808 100644
--- a/mo/web/org_jobs.py
+++ b/mo/web/org_jobs.py
@@ -1,12 +1,14 @@
from flask import render_template, g, redirect, url_for, flash
from flask_wtf.form import FlaskForm
+import os
from sqlalchemy.orm import joinedload
+from typing import Optional
import werkzeug.exceptions
import wtforms
import mo
import mo.db as db
-from mo.jobs import TheJob, job_file_size
+from mo.jobs import TheJob
from mo.web import app
import mo.web.util
@@ -60,6 +62,16 @@ def get_job(id: int) -> db.Job:
return job
+def job_file_size(job: db.Job, name: Optional[str]) -> Optional[int]:
+ if name is None:
+ return None
+
+ try:
+ return os.path.getsize(job.file_path(name))
+ except OSError:
+ return -1
+
+
@app.route('/org/jobs/<int:id>/')
def org_job(id: int):
job = get_job(id)
@@ -72,8 +84,8 @@ def org_job(id: int):
'org_job.html',
job=job,
has_errors=has_errors,
- in_size=job_file_size(job.in_file),
- out_size=job_file_size(job.out_file),
+ in_size=job_file_size(job, job.in_file),
+ out_size=job_file_size(job, job.out_file),
)
diff --git a/mo/web/util.py b/mo/web/util.py
index 1be8eb8027ee958df4216f8bde64904d9e1f2938..1340d474460e6af5da4df06cc762acb43ae16102 100644
--- a/mo/web/util.py
+++ b/mo/web/util.py
@@ -113,7 +113,7 @@ def send_task_paper(paper: db.Paper, orig: bool = False) -> Response:
def send_job_result(job: db.Job) -> Response:
assert job.out_file is not None
- file = mo.jobs.job_file_path(job.out_file)
+ file = job.file_path(job.out_file)
if file.endswith('.zip'):
type = 'application/zip'