Skip to content
Snippets Groups Projects
Commit d0887150 authored by Simon Künzel's avatar Simon Künzel
Browse files

Fix bugs in media process

parent ae5286cb
No related branches found
No related tags found
No related merge requests found
......@@ -96,6 +96,10 @@ def _check_constraint_equal(actual_constraint: Constraint, schema_constraint: Co
and actual_constraint.name != schema_constraint.name):
return False
if actual_constraint.deferrable != schema_constraint.deferrable:
# TODO add to to_string
return False
if not isinstance(schema_constraint, ColumnCollectionConstraint):
print(f"Unknown type of constraint {schema_constraint}. Can't compare")
return False
......@@ -113,13 +117,19 @@ def _check_constraint_equal(actual_constraint: Constraint, schema_constraint: Co
if not isinstance(actual_constraint, CheckConstraint):
return False
# We try our best to check if they are equal
# We try our best to check if they are equal, but with things like 'x IN ()' it breaks (because the db
# transforms the statements)
if schema_constraint.sqltext == actual_constraint.sqltext:
return True
# Sometimes the db returns it without correct brackets. Just strip them
if str(schema_constraint.sqltext).strip("()") == str(actual_constraint.sqltext).strip("()"):
return True
return False
print(f"Warning: Can't compare schema clause of constraint '{schema_constraint.name}'\n"
f" {schema_constraint.sqltext}\n"
f" with clause in database\n"
f" {actual_constraint.sqltext}")
return True
case ForeignKeyConstraint():
assert isinstance(schema_constraint, ForeignKeyConstraint) # For pycharm
......
......@@ -5,4 +5,4 @@ from .basic_targets import (
SourceFileTargetProducer,
DownscaleVideoTarget,
)
from .media_process import MediaProcess, ApiMediaProcessField
from .media_process import MediaProcess, ApiMediaProcessField, get_permanent_lecture_dir
......@@ -3,12 +3,29 @@ from collections.abc import Iterable
from videoag_common.miscellaneous import *
from videoag_common.database import *
from videoag_common.api_object import ApiSimpleColumnField, FieldContext
import videoag_common
from .target import TargetProducer, TARGET_ID_PATTERN, TARGET_ID_MAX_LENGTH
#
# Important: Json default values should be avoided in case the default value is changed and an old process relied on the default
#
_PERMANENT_MEDIA_DIR_NAME = videoag_common.config["PERMANENT_MEDIA_DIR"]
if _PERMANENT_MEDIA_DIR_NAME.endswith("/"):
raise ValueError("PERMANENT_MEDIA_DIR must NOT end with /")
# noinspection PyUnresolvedReferences
def get_permanent_lecture_dir(lecture: "Lecture") -> str:
"""
Returns the db path for the directory of the given lecture. Does NOT end with a /
"""
return (
f"{_PERMANENT_MEDIA_DIR_NAME}/"
f"course-{lecture.course.id}.{lecture.course.handle}/"
f"lecture-{lecture.id}.{lecture.time.strftime("%y%m%d")}"
)
class MediaProcess:
......
......@@ -186,7 +186,7 @@ def replace_json_arguments(data: JsonTypes, argument_data: JsonTypes) -> JsonTyp
if not isinstance(data, str):
raise TypeError(f"Unknown json type: {type(data)}")
def _sub_match(match):
def _get_match_value(match, force_string: bool = True):
path = match.group(1)
val = argument_data
for path_ele in path.split("."):
......@@ -194,7 +194,16 @@ def replace_json_arguments(data: JsonTypes, argument_data: JsonTypes) -> JsonTyp
raise ValueError(f"Can't replace with path '{path}' because element '{path_ele}' is not present "
f"(or parent has bad type) in argument data")
val = val[path_ele]
if force_string:
if isinstance(val, str) or isinstance(val, int):
val = str(val)
else:
raise ValueError(f"Can't replace with path '{path}' because result is not a str or int")
return val
return _JSON_ARGUMENT_PATTERN.sub(_sub_match, data)
full_match = _JSON_ARGUMENT_PATTERN.fullmatch(data)
if full_match is not None:
return _get_match_value(full_match, force_string=False)
return _JSON_ARGUMENT_PATTERN.sub(_get_match_value, data)
......@@ -172,7 +172,7 @@ class Lecture(DeletableApiObject, VisibilityApiObject, ApiViewPermissionsObject,
from .medium import TargetMedium
return sql.and_(
TargetMedium.lecture_id == Lecture.id,
TargetMedium.is_active(from_lecture=True)
TargetMedium.is_active(usable_check=False, from_lecture=True)
)
@staticmethod
......
from datetime import datetime
from enum import Enum
from pathlib import Path
import videoag_common
from videoag_common.database import *
from videoag_common.miscellaneous import *
from videoag_common.api_object import *
......@@ -16,6 +18,11 @@ LOAD_TARGET_MEDIUM_PUBLISH_MEDIUM = RelationshipLoadMarker()
LOAD_TARGET_MEDIUM_LECTURE = RelationshipLoadMarker()
_API_BASE_URL = videoag_common.config["API_BASE_URL"]
if _API_BASE_URL.endswith("/"):
raise ValueError("API_BASE_URL must NOT end with /")
# A file which was uploaded and needs to be assigned to a lecture, to be converted to a SourceFileTargetMedium
class SourceMedium(DeletableApiObject, Base):
__table_args__ = (
......@@ -116,6 +123,42 @@ class TargetMedium(DeletableApiObject, Base):
"polymorphic_on": "type",
"with_polymorphic": "*" # Always load all attributes for all types
}
# This isn't pretty. With a joined table inheritance it might be a bit nicer, but we couldn't check is_produced
# Also with a joined table inheritance we would have much more columns (since here we can reuse columns for different types)
__table_args__ = (
CheckConstraint(
f"type NOT IN ('plain_video', 'plain_audio') OR NOT is_produced OR duration_sec IS NOT NULL",
name="check_duration_sec_not_null"
),
CheckConstraint(
f"type NOT IN ('plain_video', 'plain_audio', 'thumbnail') OR NOT is_produced OR file_path IS NOT NULL",
name="check_file_path_not_null"
),
CheckConstraint(
f"type NOT IN ('plain_video', 'plain_audio') OR NOT is_produced OR audio_sample_rate IS NOT NULL",
name="check_audio_sample_rate_not_null"
),
CheckConstraint(
f"type NOT IN ('plain_video', 'plain_audio') OR NOT is_produced OR audio_channel_count IS NOT NULL",
name="check_audio_channel_count_not_null"
),
CheckConstraint(
f"type NOT IN ('plain_video') OR NOT is_produced OR video_vertical_resolution IS NOT NULL",
name="check_video_vertical_resolution_not_null"
),
CheckConstraint(
f"type NOT IN ('plain_video') OR NOT is_produced OR video_horizontal_resolution IS NOT NULL",
name="check_video_horizontal_resolution_not_null"
),
CheckConstraint(
f"type NOT IN ('plain_video') OR NOT is_produced OR video_frame_rate_numerator IS NOT NULL",
name="check_video_frame_rate_numerator_not_null"
),
CheckConstraint(
f"type NOT IN ('plain_video') OR NOT is_produced OR video_frame_rate_denominator IS NOT NULL",
name="check_video_frame_rate_denominator_not_null"
),
)
# Note that these four may NOT be unique. E.g. if one output is generated twice (because another output of the same
# producer was deleted, etc.)
......@@ -210,13 +253,11 @@ class FileMedium(ApiObject):
data_notes="URL to the mediums's file (Maybe with a redirect)"
)
def url(self) -> str:
# This is not too nice and only works from the API, but we need to access the API config
import api
return f"{api.config['API_BASE_URL']}/resources/target_medium/{self.id}"
return f"{_API_BASE_URL}/resources/target_medium/{self.id}"
def get_default_file_path_no_ending(self):
# TODO path
return f"{self.process_target_id}.{self.id}"
assert isinstance(self, TargetMedium)
return f"{get_permanent_lecture_dir(self.lecture)}/target-{self.id}.{self.process_target_id}"
class SingleAudioContainingMedium(ApiObject):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment