Merge remote-tracking branch 'upstream/release_23.1' into dev

This commit is contained in:
Dannon Baker
2023-11-28 22:49:01 -05:00
17 changed files with 477 additions and 20 deletions
@@ -962,6 +962,13 @@
<datatype extension="castep" type="galaxy.datatypes.text:Castep" display_in_upload="true"/>
<datatype extension="param" type="galaxy.datatypes.text:Param" display_in_upload="true"/>
<datatype extension="den_fmt" type="galaxy.datatypes.text:FormattedDensity" display_in_upload="true"/>
<!-- Larch types -->
<datatype extension="prj" type="galaxy.datatypes.larch:AthenaProject" display_in_upload="true" description="Athena project file."/>
<datatype extension="inp" type="galaxy.datatypes.larch:FEFFInput" display_in_upload="true" description="FEFF input file."/>
<datatype extension="sp" type="galaxy.datatypes.tabular:CSV" subclass="true" description="CSV representing selected FEFF paths."/>
<datatype extension="gds" type="galaxy.datatypes.tabular:CSV" subclass="true" description="CSV representing GDS parameters."/>
<datatype extension="feff" type="galaxy.datatypes.tabular:CSV" subclass="true" description="CSV representing summary of FEFF paths."/>
<datatype extension="feffit" type="galaxy.datatypes.data:Text" subclass="true" description="Report of Artemis FEFFIT."/>
<!-- ECOLOGY types -->
<datatype extension="bil" type="galaxy.datatypes.binary:Binary" mimetype="application/octet-stream" display_in_upload="true" subclass="true" description="ENVI file with band interleave by line (BIL) format"/>
<datatype extension="hdr" type="galaxy.datatypes.data:Text" mimetype="text/plain" display_in_upload="true" subclass="true" description="ENVI metadata header file"/>
@@ -1224,6 +1231,8 @@
<sniffer type="galaxy.datatypes.media:Mpg"/>
<sniffer type="galaxy.datatypes.speech:TextGrid" />
<sniffer type="galaxy.datatypes.speech:BPF" />
<sniffer type="galaxy.datatypes.larch:FEFFInput" />
<sniffer type="galaxy.datatypes.larch:AthenaProject" />
<sniffer type="galaxy.datatypes.text:Castep" />
<sniffer type="galaxy.datatypes.text:CTLresult"/>
<sniffer type="galaxy.datatypes.text:FormattedDensity" />
+218
View File
@@ -0,0 +1,218 @@
from typing import List
from galaxy.datatypes.data import (
get_file_peek,
Text,
)
from galaxy.datatypes.metadata import MetadataElement
from galaxy.datatypes.protocols import DatasetProtocol
from galaxy.datatypes.sniff import (
build_sniff_from_prefix,
FilePrefix,
get_headers,
)
@build_sniff_from_prefix
class AthenaProject(Text):
"""
Athena project format
"""
file_ext = "prj"
compressed = True
compressed_format = "gzip"
MetadataElement(
name="atsym",
desc="Atom symbol",
readonly=True,
visible=True,
)
MetadataElement(
name="bkg_e0",
desc="Edge energy (eV)",
readonly=True,
visible=True,
)
MetadataElement(
name="edge",
desc="Edge",
readonly=True,
visible=True,
)
MetadataElement(
name="npts",
desc="Number of points",
readonly=True,
visible=True,
)
MetadataElement(
name="xmax",
desc="Maximum energy (eV)",
readonly=True,
visible=True,
)
MetadataElement(
name="xmin",
desc="Minimum energy (eV)",
readonly=True,
visible=True,
)
def sniff_prefix(self, file_prefix: FilePrefix) -> bool:
"""
Try to guess if the file is an Athena project file.
>>> from galaxy.datatypes.sniff import get_test_fname
>>> fname = get_test_fname('test.prj')
>>> AthenaProject().sniff(fname)
True
>>> from galaxy.datatypes.sniff import get_test_fname
>>> fname = get_test_fname('Si.cif')
>>> AthenaProject().sniff(fname)
False
"""
return file_prefix.startswith("# Athena project file")
def set_meta(self, dataset: DatasetProtocol, *, overwrite: bool = True, **kwd) -> None:
"""
Extract metadata from @args
"""
def extract_arg(args: List[str], arg_name: str):
try:
index = args.index(f"'{arg_name}'")
setattr(dataset.metadata, arg_name, args[index + 1].replace("'", ""))
except ValueError:
return
headers = get_headers(dataset.file_name, sep=" = ", count=3, comment_designator="#")
args = []
for header in headers:
if header[0] == "@args":
args = header[1][1:-2].split(",")
break
extract_arg(args, "atsym")
extract_arg(args, "bkg_e0")
extract_arg(args, "edge")
extract_arg(args, "npts")
extract_arg(args, "xmax")
extract_arg(args, "xmin")
def set_peek(self, dataset: DatasetProtocol, **kwd) -> None:
if not dataset.dataset.purged:
dataset.peek = get_file_peek(dataset.file_name)
dataset.info = (
f"atsym: {dataset.metadata.atsym}\n"
f"bkg_e0: {dataset.metadata.bkg_e0}\n"
f"edge: {dataset.metadata.edge}\n"
f"npts: {dataset.metadata.npts}\n"
f"xmax: {dataset.metadata.xmax}\n"
f"xmin: {dataset.metadata.xmin}"
)
dataset.blurb = f"Athena project file of {dataset.metadata.atsym} {dataset.metadata.edge} edge"
else:
dataset.peek = "file does not exist"
dataset.blurb = "file purged from disk"
@build_sniff_from_prefix
class FEFFInput(Text):
"""
FEFF input format
"""
file_ext = "inp"
MetadataElement(
name="title_block",
desc="Title block",
readonly=True,
visible=True,
)
def sniff_prefix(self, file_prefix: FilePrefix) -> bool:
"""
Try to guess if the file is an FEFF input file.
>>> from galaxy.datatypes.sniff import get_test_fname
>>> fname = get_test_fname('larch_pymatgen.inp')
>>> FEFFInput().sniff(fname)
True
>>> from galaxy.datatypes.sniff import get_test_fname
>>> fname = get_test_fname('larch_atoms.inp')
>>> FEFFInput().sniff(fname)
True
>>> from galaxy.datatypes.sniff import get_test_fname
>>> fname = get_test_fname('larch_potentials.inp')
>>> FEFFInput().sniff(fname)
True
>>> from galaxy.datatypes.sniff import get_test_fname
>>> fname = get_test_fname('larch_bad_atoms.txt')
>>> FEFFInput().sniff(fname)
False
>>> from galaxy.datatypes.sniff import get_test_fname
>>> fname = get_test_fname('larch_bad_potentials.txt')
>>> FEFFInput().sniff(fname)
False
>>> from galaxy.datatypes.sniff import get_test_fname
>>> fname = get_test_fname('Si.cif')
>>> FEFFInput().sniff(fname)
False
"""
# pymatgen marks generated FEFF inputs, but user might also upload from another source
if file_prefix.startswith("* This FEFF.inp file generated by pymatgen"):
return True
generator = file_prefix.line_iterator()
try:
line = next(generator).strip()
while line is not None:
if line == "POTENTIALS":
line = next(generator).strip()
if line[0] == "*":
words = line[1:].split()
if (words[0] in ["potential-index", "ipot"]) and (words[1] == "Z"):
return True
return False
elif line == "ATOMS":
line = next(generator).strip()
if line[0] == "*":
words = line[1:].split()
if words[:4] == ["x", "y", "z", "ipot"]:
return True
return False
else:
line = next(generator).strip()
except StopIteration:
return False
return False
def set_meta(self, dataset: DatasetProtocol, overwrite: bool = True, **kwd) -> None:
"""
Extract metadata from TITLE
"""
title_block = ""
headers = get_headers(dataset.file_name, sep=None, comment_designator="*")
for header in headers:
if header and header[0] == "TITLE":
title_block += " ".join(header[1:]) + "\n"
dataset.metadata.title_block = title_block
def set_peek(self, dataset: DatasetProtocol, **kwd) -> None:
if not dataset.dataset.purged:
dataset.peek = get_file_peek(dataset.file_name)
dataset.info = dataset.metadata.title_block
else:
dataset.peek = "file does not exist"
dataset.blurb = "file purged from disk"
+44
View File
@@ -0,0 +1,44 @@
TITLE comment: None given
TITLE Source:
TITLE Structure Summary: Fe2 S4
TITLE Reduced formula: FeS2
TITLE space group: (Pnnm), space number: (58)
TITLE abc: 3.385200 4.447400 5.428700
TITLE angles: 90.000000 90.000000 90.000000
TITLE sites: 6
* 1 Fe 0.000000 0.000000 0.000000
* 2 Fe 0.500000 0.500000 0.500000
* 3 S 0.000000 0.199900 0.378040
* 4 S 0.000000 0.800100 0.621960
* 5 S 0.500000 0.699900 0.121960
* 6 S 0.500000 0.300100 0.878040
ATOMS
* x y z ipot Atom Distance Number
**********- ********- ******- ****** ****** ********** ********
0 0 0 0 Fe 0 0
-0.889035 -2.05227 -0 2 S 2.23656 4
0.889035 2.05227 0 2 S 2.23656 22
-1.33466 0.662084 -1.6926 2 S 2.2549 6
-1.33466 0.662084 1.6926 2 S 2.2549 8
1.33466 -0.662084 -1.6926 2 S 2.2549 11
1.33466 -0.662084 1.6926 2 S 2.2549 13
0 0 -3.3852 1 Fe 3.3852 18
0 0 3.3852 1 Fe 3.3852 19
-0.889035 3.37643 0 2 S 3.49152 9
0.889035 -3.37643 -0 2 S 3.49152 15
-3.11273 -0.662084 -1.6926 2 S 3.60449 1
-3.11273 -0.662084 1.6926 2 S 3.60449 3
3.11273 0.662084 -1.6926 2 S 3.60449 16
3.11273 0.662084 1.6926 2 S 3.60449 20
-2.2237 -2.71435 -1.6926 1 Fe 3.89582 2
-2.2237 -2.71435 1.6926 1 Fe 3.89582 5
-2.2237 2.71435 -1.6926 1 Fe 3.89582 7
-2.2237 2.71435 1.6926 1 Fe 3.89582 10
2.2237 -2.71435 -1.6926 1 Fe 3.89582 12
2.2237 -2.71435 1.6926 1 Fe 3.89582 14
2.2237 2.71435 -1.6926 1 Fe 3.89582 17
2.2237 2.71435 1.6926 1 Fe 3.89582 21
END
@@ -0,0 +1,44 @@
TITLE comment: None given
TITLE Source:
TITLE Structure Summary: Fe2 S4
TITLE Reduced formula: FeS2
TITLE space group: (Pnnm), space number: (58)
TITLE abc: 3.385200 4.447400 5.428700
TITLE angles: 90.000000 90.000000 90.000000
TITLE sites: 6
* 1 Fe 0.000000 0.000000 0.000000
* 2 Fe 0.500000 0.500000 0.500000
* 3 S 0.000000 0.199900 0.378040
* 4 S 0.000000 0.800100 0.621960
* 5 S 0.500000 0.699900 0.121960
* 6 S 0.500000 0.300100 0.878040
ATOMS
* a b c ipot Atom Distance Number
**********- ********- ******- ****** ****** ********** ********
0 0 0 0 Fe 0 0
-0.889035 -2.05227 -0 2 S 2.23656 4
0.889035 2.05227 0 2 S 2.23656 22
-1.33466 0.662084 -1.6926 2 S 2.2549 6
-1.33466 0.662084 1.6926 2 S 2.2549 8
1.33466 -0.662084 -1.6926 2 S 2.2549 11
1.33466 -0.662084 1.6926 2 S 2.2549 13
0 0 -3.3852 1 Fe 3.3852 18
0 0 3.3852 1 Fe 3.3852 19
-0.889035 3.37643 0 2 S 3.49152 9
0.889035 -3.37643 -0 2 S 3.49152 15
-3.11273 -0.662084 -1.6926 2 S 3.60449 1
-3.11273 -0.662084 1.6926 2 S 3.60449 3
3.11273 0.662084 -1.6926 2 S 3.60449 16
3.11273 0.662084 1.6926 2 S 3.60449 20
-2.2237 -2.71435 -1.6926 1 Fe 3.89582 2
-2.2237 -2.71435 1.6926 1 Fe 3.89582 5
-2.2237 2.71435 -1.6926 1 Fe 3.89582 7
-2.2237 2.71435 1.6926 1 Fe 3.89582 10
2.2237 -2.71435 -1.6926 1 Fe 3.89582 12
2.2237 -2.71435 1.6926 1 Fe 3.89582 14
2.2237 2.71435 -1.6926 1 Fe 3.89582 17
2.2237 2.71435 1.6926 1 Fe 3.89582 21
END
@@ -0,0 +1,24 @@
TITLE comment: None given
TITLE Source:
TITLE Structure Summary: Fe2 S4
TITLE Reduced formula: FeS2
TITLE space group: (Pnnm), space number: (58)
TITLE abc: 3.385200 4.447400 5.428700
TITLE angles: 90.000000 90.000000 90.000000
TITLE sites: 6
* 1 Fe 0.000000 0.000000 0.000000
* 2 Fe 0.500000 0.500000 0.500000
* 3 S 0.000000 0.199900 0.378040
* 4 S 0.000000 0.800100 0.621960
* 5 S 0.500000 0.699900 0.121960
* 6 S 0.500000 0.300100 0.878040
POTENTIALS
* pot Z tag lmax1 lmax2 xnatph(stoichometry) spinph
******- **- ****- ******- ******- ********************** ********
0 26 Fe -1 -1 0.0001 0
1 26 Fe -1 -1 2 0
2 16 S -1 -1 4 0
END
@@ -0,0 +1,24 @@
TITLE comment: None given
TITLE Source:
TITLE Structure Summary: Fe2 S4
TITLE Reduced formula: FeS2
TITLE space group: (Pnnm), space number: (58)
TITLE abc: 3.385200 4.447400 5.428700
TITLE angles: 90.000000 90.000000 90.000000
TITLE sites: 6
* 1 Fe 0.000000 0.000000 0.000000
* 2 Fe 0.500000 0.500000 0.500000
* 3 S 0.000000 0.199900 0.378040
* 4 S 0.000000 0.800100 0.621960
* 5 S 0.500000 0.699900 0.121960
* 6 S 0.500000 0.300100 0.878040
POTENTIALS
*ipot Z tag lmax1 lmax2 xnatph(stoichometry) spinph
******- **- ****- ******- ******- ********************** ********
0 26 Fe -1 -1 0.0001 0
1 26 Fe -1 -1 2 0
2 16 S -1 -1 4 0
END
@@ -0,0 +1,52 @@
* This FEFF.inp file generated by pymatgen
TITLE comment: None given
TITLE Source:
TITLE Structure Summary: Fe2 S4
TITLE Reduced formula: FeS2
TITLE space group: (Pnnm), space number: (58)
TITLE abc: 3.385200 4.447400 5.428700
TITLE angles: 90.000000 90.000000 90.000000
TITLE sites: 6
* 1 Fe 0.000000 0.000000 0.000000
* 2 Fe 0.500000 0.500000 0.500000
* 3 S 0.000000 0.199900 0.378040
* 4 S 0.000000 0.800100 0.621960
* 5 S 0.500000 0.699900 0.121960
* 6 S 0.500000 0.300100 0.878040
POTENTIALS
*ipot Z tag lmax1 lmax2 xnatph(stoichometry) spinph
******- **- ****- ******- ******- ********************** ********
0 26 Fe -1 -1 0.0001 0
1 26 Fe -1 -1 2 0
2 16 S -1 -1 4 0
ATOMS
* x y z ipot Atom Distance Number
**********- ********- ******- ****** ****** ********** ********
0 0 0 0 Fe 0 0
-0.889035 -2.05227 -0 2 S 2.23656 4
0.889035 2.05227 0 2 S 2.23656 22
-1.33466 0.662084 -1.6926 2 S 2.2549 6
-1.33466 0.662084 1.6926 2 S 2.2549 8
1.33466 -0.662084 -1.6926 2 S 2.2549 11
1.33466 -0.662084 1.6926 2 S 2.2549 13
0 0 -3.3852 1 Fe 3.3852 18
0 0 3.3852 1 Fe 3.3852 19
-0.889035 3.37643 0 2 S 3.49152 9
0.889035 -3.37643 -0 2 S 3.49152 15
-3.11273 -0.662084 -1.6926 2 S 3.60449 1
-3.11273 -0.662084 1.6926 2 S 3.60449 3
3.11273 0.662084 -1.6926 2 S 3.60449 16
3.11273 0.662084 1.6926 2 S 3.60449 20
-2.2237 -2.71435 -1.6926 1 Fe 3.89582 2
-2.2237 -2.71435 1.6926 1 Fe 3.89582 5
-2.2237 2.71435 -1.6926 1 Fe 3.89582 7
-2.2237 2.71435 1.6926 1 Fe 3.89582 10
2.2237 -2.71435 -1.6926 1 Fe 3.89582 12
2.2237 -2.71435 1.6926 1 Fe 3.89582 14
2.2237 2.71435 -1.6926 1 Fe 3.89582 17
2.2237 2.71435 1.6926 1 Fe 3.89582 21
END
Binary file not shown.
+17
View File
@@ -4943,6 +4943,20 @@ class HistoryDatasetAssociation(DatasetInstance, HasTags, Dictifiable, UsesAnnot
self.copied_from_history_dataset_association = copied_from_history_dataset_association
self.copied_from_library_dataset_dataset_association = copied_from_library_dataset_dataset_association
def __strict_check_before_flush__(self):
if self.extension != "len":
# TODO: Custom builds (with .len extension) do not get a history or a HID.
# These should get some other type of permanent storage, perhaps UserDatasetAssociation ?
# Everything else needs to have a hid and a history
if not self.history and not getattr(self, "history_id", None):
raise Exception(f"HistoryDatasetAssociation {self} without history detected, this is not valid")
elif not self.hid:
raise Exception(f"HistoryDatasetAssociation {self} without hid, this is not valid")
elif self.dataset.file_size is None and self.dataset.state not in self.dataset.no_data_states:
raise Exception(
f"HistoryDatasetAssociation {self} in state {self.dataset.state} with null file size, this is not valid"
)
@property
def user(self):
if self.history:
@@ -7021,6 +7035,9 @@ class DatasetCollectionElement(Base, Dictifiable, Serializable):
self.element_index = element_index
self.element_identifier = element_identifier or str(element_index)
def __strict_check_before_flush__(self):
assert self.element_object, "Dataset Collection Element without child entity detected, this is not valid"
@property
def element_type(self):
if self.hda:
+6 -12
View File
@@ -3,6 +3,7 @@ Shared model and mapping code between Galaxy and Tool Shed, trying to
generalize to generic database connections.
"""
import contextlib
import logging
import os
import threading
from contextvars import ContextVar
@@ -29,6 +30,8 @@ from galaxy.util.bunch import Bunch
if TYPE_CHECKING:
from galaxy.model.store import SessionlessContext
log = logging.getLogger(__name__)
# Create a ContextVar with mutable state, this allows sync tasks in the context
# of a request (which run within a threadpool) to see changes to the ContextVar
# state. See https://github.com/tiangolo/fastapi/issues/953#issuecomment-586006249
@@ -146,23 +149,14 @@ def versioned_objects(iter):
def versioned_objects_strict(iter):
for obj in iter:
if hasattr(obj, "__strict_check_before_flush__"):
obj.__strict_check_before_flush__()
if hasattr(obj, "__create_version__"):
if obj.extension != "len":
# TODO: Custom builds (with .len extension) do not get a history or a HID.
# These should get some other type of permanent storage, perhaps UserDatasetAssociation ?
# Everything else needs to have a hid and a history
if not obj.history and not obj.history_id:
raise Exception(f"HistoryDatasetAssociation {obj} without history detected, this is not valid")
elif not obj.hid:
raise Exception(f"HistoryDatasetAssociation {obj} without hid, this is not valid")
elif obj.dataset.file_size is None and obj.dataset.state not in obj.dataset.no_data_states:
raise Exception(
f"HistoryDatasetAssociation {obj} in state {obj.dataset.state} with null file size, this is not valid"
)
yield obj
if os.environ.get("GALAXY_TEST_RAISE_EXCEPTION_ON_HISTORYLESS_HDA"):
log.debug("Using strict flush checks")
versioned_objects = versioned_objects_strict # noqa: F811
+8 -5
View File
@@ -21,7 +21,10 @@ from galaxy.schema.fields import (
EncodedDatabaseIdField,
)
from galaxy.schema.schema import Model
from galaxy.schema.types import AbsoluteOrRelativeUrl
from galaxy.schema.types import (
AbsoluteOrRelativeUrl,
OffsetNaiveDatetime,
)
class NotificationVariant(str, Enum):
@@ -246,12 +249,12 @@ class NotificationCreateData(Model):
category: NotificationCategory = NotificationCategoryField
variant: NotificationVariant = NotificationVariantField
content: AnyNotificationContent
publication_time: Optional[datetime] = Field(
publication_time: Optional[OffsetNaiveDatetime] = Field(
None,
title="Publication time",
description="The time when the notification should be published. Notifications can be created and then scheduled to be published at a later time.",
)
expiration_time: Optional[datetime] = Field(
expiration_time: Optional[OffsetNaiveDatetime] = Field(
None,
title="Expiration time",
description="The time when the notification should expire. By default it will expire after 6 months. Expired notifications will be permanently deleted.",
@@ -351,12 +354,12 @@ class NotificationBroadcastUpdateRequest(NotificationUpdateRequest):
title="Variant",
description="The variant of the notification. Used to express the importance of the notification.",
)
publication_time: Optional[datetime] = Field(
publication_time: Optional[OffsetNaiveDatetime] = Field(
None,
title="Publication time",
description="The time when the notification should be published. Notifications can be created and then scheduled to be published at a later time.",
)
expiration_time: Optional[datetime] = Field(
expiration_time: Optional[OffsetNaiveDatetime] = Field(
None,
title="Expiration time",
description="The time when the notification should expire. By default it will expire after 6 months. Expired notifications will be permanently deleted.",
+5 -1
View File
@@ -548,6 +548,9 @@ def send_file(start_response, trans, body):
trans.response.headers["accept-ranges"] = "bytes"
start = None
end = None
if trans.request.method == "HEAD":
trans.response.headers["content-length"] = os.path.getsize(body.name)
body = b""
if trans.request.range:
start = int(trans.request.range.start)
file_size = int(trans.response.headers["content-length"])
@@ -555,7 +558,8 @@ def send_file(start_response, trans, body):
trans.response.headers["content-length"] = str(end - start)
trans.response.headers["content-range"] = f"bytes {start}-{end - 1}/{file_size}"
trans.response.status = 206
body = iterate_file(body, start, end)
if body:
body = iterate_file(body, start, end)
start_response(trans.response.wsgi_status(), trans.response.wsgi_headeritems())
return body
@@ -3,7 +3,6 @@ API operations on Notification objects.
"""
import logging
from datetime import datetime
from typing import Optional
from fastapi import (
@@ -33,6 +32,7 @@ from galaxy.schema.notifications import (
UserNotificationsBatchUpdateRequest,
UserNotificationUpdateRequest,
)
from galaxy.schema.types import OffsetNaiveDatetime
from galaxy.webapps.galaxy.services.notifications import NotificationService
from . import (
depends,
@@ -56,7 +56,7 @@ class FastAPINotifications:
def get_notifications_status(
self,
trans: ProvidesUserContext = DependsOnTrans,
since: datetime = Query(),
since: OffsetNaiveDatetime = Query(),
) -> NotificationStatusSummary:
"""Anonymous users cannot receive personal notifications, only broadcasted notifications."""
return self.service.get_notifications_status(trans, since)
+8
View File
@@ -933,6 +933,14 @@ def populate_api_routes(webapp, app):
parent_resources=dict(member_name="job", collection_name="jobs"),
)
webapp.mapper.connect(
"index",
"/api/jobs/{job_id}/files",
controller="job_files",
action="index",
conditions=dict(method=["HEAD"]),
)
webapp.mapper.resource(
"port",
"ports",
+2
View File
@@ -880,6 +880,8 @@ class GalaxyTestDriver(TestDriver):
"""Setup various variables used to launch a Galaxy server."""
config_object = self._ensure_config_object(config_object)
self.external_galaxy = os.environ.get("GALAXY_TEST_EXTERNAL", None)
if not self.external_galaxy:
os.environ["GALAXY_TEST_STRICT_CHECKS"] = "1"
# Allow controlling the log format
self.log_format = os.environ.get("GALAXY_TEST_LOG_FORMAT")
+4
View File
@@ -63,6 +63,10 @@ class TestJobFilesIntegration(integration_util.IntegrationTestCase):
job_id, job_key = self._api_job_keys(job)
data = {"path": self.input_hda.get_file_name(), "job_key": job_key}
get_url = self._api_url(f"jobs/{job_id}/files", use_key=True)
head_response = requests.head(get_url, params=data)
api_asserts.assert_status_code_is_ok(head_response)
assert head_response.text == ""
assert head_response.headers["content-length"] == str(len(TEST_INPUT_TEXT))
response = requests.get(get_url, params=data)
api_asserts.assert_status_code_is_ok(response)
assert response.text == TEST_INPUT_TEXT
+10
View File
@@ -269,6 +269,16 @@ class TestNotificationsIntegration(IntegrationTestCase):
assert "Scheduled" in subjects
assert "Expired" in subjects
def test_notification_input_dates_consider_timezone(self):
payload = notification_broadcast_test_data(subject="Test", message="Test")
payload["publication_time"] = "2021-01-01T12:00:00+02:00"
payload["expiration_time"] = "2021-01-01T12:00:00Z"
response = self._post("notifications/broadcast", data=payload, admin=True, json=True)
self._assert_status_code_is_ok(response)
notification = response.json()["notification"]
assert notification["publication_time"] == "2021-01-01T10:00:00"
assert notification["expiration_time"] == "2021-01-01T12:00:00"
def test_broadcast_notification_action_links(self):
# Broadcast notifications can have relative and absolute links
response = self._send_broadcast_notification(