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

Fix handling of legacy semester string, Closes #23

parent 97ad69b8
No related branches found
No related tags found
No related merge requests found
Pipeline #5873 passed
......@@ -24,9 +24,9 @@ COURSE_SECONDARY_DB_SELECTION = f"""
def _semester_db_to_json(semester_db: str):
if semester_db is None or semester_db.isspace():
if semester_db is None or len(semester_db) == 0 or semester_db.isspace():
return "none" # pragma: no cover
from api.objects.types import SEMESTER_STRING_PATTERN
from api.objects.type import SEMESTER_STRING_PATTERN
if SEMESTER_STRING_PATTERN.fullmatch(semester_db) is None: # pragma: no cover
print(f"Warning: Invalid semester string in database: {truncate_string(semester_db)}")
return "none"
......
......@@ -6,6 +6,7 @@ from api.objects.changelog import ChangelogVersionBuilder, ChangelogEntryError,
from api.objects.changelog_entries import ModificationEntry, DeletionEntry
from api.objects.field import ObjectField
from api.objects.objects import *
from api.objects.type import SEMESTER_STRING_PATTERN
def _encode_identity(value_json: str) -> str:
......@@ -86,11 +87,21 @@ def _decode_legacy_string_to_id_string(legacy_value: str) -> str:
def _decode_legacy_string_to_semester_string(legacy_value: str) -> str:
if legacy_value is None:
raise ChangelogEntryError("Cannot encode null values")
if len(legacy_value) == 0 or legacy_value.isspace():
return "none"
if len(legacy_value) > 100 or SEMESTER_STRING_PATTERN.fullmatch(legacy_value) is None:
raise ChangelogEntryError(f"Legacy string is not a semester string: '{truncate_string(legacy_value)}'")
return legacy_value
def _encode_legacy_semester_string(value_json: str) -> str:
if value_json is None:
raise ChangelogEntryError("Cannot encode null values")
if value_json == "none":
return ""
return value_json
def _decode_legacy_announcement_level(legacy_value: str) -> str:
level = _decode_legacy_integer_non_negative(legacy_value)
match level:
......@@ -164,7 +175,7 @@ class _DeletionEntryCodec(ChangelogEntryCodec[SimpleChangelogEntrySubject]):
_CODEC_BOOLEAN = _LegacyModificationEntryCodec(_decode_legacy_boolean, _encode_legacy_boolean)
_CODEC_STRING_SHORT = _LegacyModificationEntryCodec(_decode_legacy_shortstring_to_string_short, _encode_identity)
_CODEC_STRING_TO_ID_STRING = _LegacyModificationEntryCodec(_decode_legacy_string_to_id_string, _encode_identity)
_CODEC_STRING_TO_SEMESTER_STRING = _LegacyModificationEntryCodec(_decode_legacy_string_to_semester_string, _encode_identity)
_CODEC_STRING_TO_SEMESTER_STRING = _LegacyModificationEntryCodec(_decode_legacy_string_to_semester_string, _encode_legacy_semester_string)
_CODEC_TEXT_TO_STRING_LONG = _LegacyModificationEntryCodec(_decode_legacy_text_to_string_long, _encode_identity)
_CODEC_DATETIME = _LegacyModificationEntryCodec(_decode_legacy_datetime, _encode_legacy_datetime)
_CODEC_INTEGER_NON_NEGATIVE = _LegacyModificationEntryCodec(_decode_legacy_integer_non_negative, _encode_legacy_integer)
......
......@@ -308,6 +308,39 @@ class MappedStringType(AbstractStringType):
return self.__json_to_db[string]
SEMESTER_STRING_REGEX = "([0-9]{4}(ws|ss)|none)"
SEMESTER_STRING_PATTERN = re.compile(SEMESTER_STRING_REGEX)
class SemesterStringType(AbstractStringType):
def __init__(self):
super().__init__("semester_string", False)
def get_max_string_length(self) -> int:
"""Returns the exact maximum length of this string"""
return 6
def db_value_to_json(self, db_value: str) -> str:
if len(db_value) == 0 or db_value.isspace():
return "none"
if SEMESTER_STRING_PATTERN.fullmatch(db_value) is None:
print(f"Warning: Invalid semester string in database: {truncate_string(db_value)}")
return "none"
return db_value
def validate_and_convert_client_value(self,
transaction: AbstractTransaction,
object_id: int or None,
client_value: CJsonValue) -> str or None:
value = client_value.as_string(50, 0)
if SEMESTER_STRING_PATTERN.fullmatch(value) is None:
client_value.raise_error("String does not match pattern")
if value == "none":
value = ""
return value
class DatetimeType(AbstractStringType):
def __init__(self, may_be_none: bool = False):
......
import re
from api.miscellaneous import ID_STRING_REGEX_NO_LENGTH
from api.objects.type import (NumberType, IntType, LongType,
from api.objects.type import (IntType, LongType,
SimpleType, ObjectIdType,
StringType, SimpleStringType, UniqueStringType, MappedStringType,
StringType, SimpleStringType, UniqueStringType, MappedStringType, SemesterStringType,
DatetimeType)
SEMESTER_STRING_REGEX = "([0-9]{4}(ws|ss)|none)"
SEMESTER_STRING_PATTERN = re.compile(SEMESTER_STRING_REGEX)
TYPE_INT = IntType()
TYPE_LONG = LongType()
TYPE_BOOLEAN = SimpleType("boolean", lambda db: bool(db), lambda json_value: json_value.as_bool())
......@@ -24,7 +20,7 @@ TYPE_COURSE_ID_STRING = UniqueStringType(
"id",
"handle"
)
TYPE_SEMESTER_STRING = SimpleStringType("semester_string", 6, 0, SEMESTER_STRING_REGEX) # Max length for safety
TYPE_SEMESTER_STRING = SemesterStringType()
TYPE_DURATION = IntType(0, 2147483647)
TYPE_DATETIME = DatetimeType()
TYPE_TIMESTAMP = IntType(0, 2147483647)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment