Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f2985dc
added common app usage documentation
keighrim May 21, 2023
d77e7e9
added support for --scheme
leorabaumgarten Jun 26, 2023
a54984c
added a check for value validation for choice-based runtime param
keighrim Jun 26, 2023
b0a5e23
fixed scheme support
leorabaumgarten Jun 27, 2023
58bd12e
modified and added tests after scheme support
leorabaumgarten Jun 27, 2023
04c2c2b
add updates to user manual
wricketts Jun 27, 2023
0170ae8
fix a typo
wricketts Jun 27, 2023
bf1643c
add updates to documentation
wricketts Jun 28, 2023
bdcb22e
add updates to documentation
wricketts Jun 28, 2023
9b275e5
add updates to documentation
wricketts Jun 28, 2023
c6ca4f4
Merge branch '161-merge-httpsgithubcomclamsprojectapp-nlp-example-1' …
keighrim Jun 28, 2023
438dc2e
reverted changes in `target-vers.csv`, will be re-gened at next build
keighrim Jun 29, 2023
974c030
updated some wordings
keighrim Jun 29, 2023
b5db906
added support for mixed schemes with --scheme
leorabaumgarten Jun 30, 2023
ecc49df
relaxing mmif-python version, taking advangtage of looser individuate…
keighrim Jun 23, 2023
55de056
upgraded opencv-python version in the opencv container ...
keighrim Jun 26, 2023
ffd1e13
minor updates in the template documentation
keighrim Jun 26, 2023
e921b14
Merge pull request #164 from clamsproject/160-fix-params-w-choices
keighrim Jun 30, 2023
9a2950d
updated test cases for source CLI
keighrim Jul 1, 2023
197027e
Merge pull request #163 from clamsproject/162-uri-scheme-support
keighrim Jul 1, 2023
83888ee
minor fix in testCLI for portability
keighrim Jul 1, 2023
55fbd53
add tutorial.md and modify conf.py
wricketts Jul 6, 2023
1d15282
updated tutorial document
keighrim Jul 7, 2023
3fcbfe7
Merge branch 'develop' into 161-nnew
keighrim Jul 7, 2023
62c5707
Merge pull request #165 from clamsproject/161-merge-httpsgithubcomcla…
keighrim Jul 7, 2023
e6a8be2
renamed readme for devs in GHA template, why? see ...
keighrim Jul 8, 2023
6d84ba5
fixed triggers of issue-mngt-wf in GHA template for external PRs
keighrim Jul 8, 2023
efdebbd
updated to the latest mmif-python
keighrim Jul 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clams/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ def get_configuration(self, **runtime_params):
conf = {}
for parameter in self.metadata.parameters:
if parameter.name in runtime_params:
if parameter.choices and runtime_params[parameter.name] not in parameter.choices:
raise ValueError(f"Value for parameter \"{parameter.name}\" must be one of {parameter.choices}.")
conf[parameter.name] = runtime_params[parameter.name]
elif parameter.default is not None:
conf[parameter.name] = parameter.default
Expand Down
6 changes: 3 additions & 3 deletions clams/develop/templates/app/README.md.template
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This skeleton code is a scaffolding for Python-based CLAMS app development. Spec
1. `app.py` and `metadata.py` to write the app
1. `requirements.txt` to specify python dependencies
1. `Containerfile` to containerize the app and specify system dependencies
1. `.gitignore` and `.dorckrignore` files listing commonly ignored files
1. `.gitignore` and `.dockerignore` files listing commonly ignored files
1. an empty `LICENSE` file to replace with an actual license information of the app
1. `CLAMS-generic-readme.md` file with basic instructions of app installation and execution
1. This `README.md` file for additional information not specified in the generic readme file.
Expand All @@ -24,7 +24,7 @@ Then use the following section to document any additional information specific t

## User instruction

General user instruction for CLAMS apps is available at [CLAMS Apps documentation](https://apps.clams.ai/clamsapp/).
General user instructions for CLAMS apps is available at [CLAMS Apps documentation](https://apps.clams.ai/clamsapp).

Below is a list of additional information specific to this app.

Expand All @@ -34,4 +34,4 @@ Below is a list of additional information specific to this app.

### Configurable runtime parameter

(Parameters should be already well-described in the app metadata. But you can use this space to show examples, for instance.)
(Parameters should be already well-described in the app metadata. But you can use this space to show examples, for instance.)
24 changes: 22 additions & 2 deletions clams/develop/templates/app/app.py.template
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
"""
DELETE THIS MODULE STRING AND REPLACE IT WITH A DESCRIPTION OF YOUR APP.

app.py Template

The app.py script does several things:
- import the necessary code
- create a subclass of ClamsApp that defines the metadata and provides a method to run the wrapped NLP tool
- provide a way to run the code as a RESTful Flask service


"""

import argparse
from typing import Union

# mostly likely you'll need these modules/classes
# Imports needed for Clams and MMIF.
# Non-NLP Clams applications will require AnnotationTypes

from clams import ClamsApp, Restifier
from mmif import Mmif, View, Annotation, Document, AnnotationTypes, DocumentTypes

# For an NLP tool we need to import the LAPPS vocabulary items
from lapps.discriminators import Uri


class $APP_CLASS_NAME(ClamsApp):

Expand All @@ -28,7 +46,7 @@ if __name__ == "__main__":
"--port", action="store", default="5000", help="set port to listen"
)
parser.add_argument("--production", action="store_true", help="run gunicorn server")
# more arguments as needed
# add more arguments as needed
# parser.add_argument(more_arg...)

parsed_args = parser.parse_args()
Expand All @@ -38,7 +56,9 @@ if __name__ == "__main__":

http_app = Restifier(app, port=int(parsed_args.port)
)
# for running the application in production mode
if parsed_args.production:
http_app.serve_production()
# development mode
else:
http_app.run()
11 changes: 7 additions & 4 deletions clams/develop/templates/app/metadata.py.template
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,19 @@ def appmetadata() -> AppMetadata:
description="", # briefly describe what the purpose and features of the app
app_license="", # short name for a software license like MIT, Apache2, GPL, etc.
identifier="$APP_IDENTIFIER", # should be a single string without whitespaces. If you don't intent to publish this app to the CLAMS app-directory, please use a full IRI format.
url="https://fakegithub.com/some/repository", # a website where the source code and full documentation of the app is hosted, if you are on the CLAMS team, see ``.github/README.md`` file in this directory.
# use the following if this app is a wrapper of an existing computational analysis tool
url="https://fakegithub.com/some/repository", # a website where the source code and full documentation of the app is hosted
# (if you are on the CLAMS team, this MUST be "https://github.com/clamsproject/app-$APP_IDENTIFIER"
# (see ``.github/README.md`` file in this directory for the reason)
analyzer_version='version_X', # use this IF THIS APP IS A WRAPPER of an existing computational analysis algorithm
# (it is very important to pinpoint the primary analyzer version for reproducibility)
analyzer_version='version_X',
# (for example, when the app's implementation uses ``torch``, it doesn't make the app a "torch-wrapper")
# (but, when the app doesn't implementaion any additional algorithms/model/architecture, but simply use API's of existing, for exmaple, OCR software, it is a wrapper)
# if the analyzer is a python app, and it's specified in the requirements.txt
# this trick can also be useful (replace ANALYZER_NAME with the pypi dist name)
analyzer_version=[l.strip().rsplit('==')[-1] for l in open('requirements.txt').readlines() if re.match(r'^ANALYZER_NAME==', l)][0],
analyzer_license="", # short name for a software license
)
# and then add I/O specifications: an app must have at least one input and ont output
# and then add I/O specifications: an app must have at least one input and one output
metadata.add_input(DocumentTypes.Document)
metadata.add_output(AnnotationTypes.Thing, typeSpecificProperty='property-value')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
types:
- opened
- transferred
pull_request:
pull_request_target:
types:
- opened

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
issues:
types:
- closed
pull_request:
pull_request_target:
types:
- closed

Expand Down
63 changes: 57 additions & 6 deletions clams/source/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import textwrap
from os import path
from typing import Union, Generator, List, Optional, Iterable
from urllib.parse import urlparse

from mmif import Mmif, Document, DocumentTypes, __specver__
from mmif.serialize.mmif import MmifMetadata
Expand Down Expand Up @@ -170,7 +171,7 @@ def __iter__(self):
yield self.produce()


def generate_source_mmif(documents, prefix=None, **ignored):
def generate_source_mmif_from_file(documents, prefix=None, **ignored):
from string import Template
at_types = {
'video': DocumentTypes.VideoDocument,
Expand All @@ -188,7 +189,6 @@ def generate_source_mmif(documents, prefix=None, **ignored):
pl = WorkflowSource()
if prefix and not path.isabs(prefix):
raise ValueError(f"prefix must be an absolute path; given \"{prefix}\".")

for doc_id, arg in enumerate(documents, start=1):
arg = arg.strip()
if len(arg) < 1:
Expand Down Expand Up @@ -216,6 +216,45 @@ def generate_source_mmif(documents, prefix=None, **ignored):
return pl.produce().serialize(pretty=True)


def generate_source_mmif_from_customscheme(documents, scheme, **ignored):
from string import Template
at_types = {
'video': DocumentTypes.VideoDocument,
'audio': DocumentTypes.AudioDocument,
'text': DocumentTypes.TextDocument,
'image': DocumentTypes.ImageDocument
}
template = Template('''{
"@type": "${at_type}",
"properties": {
"id": "${aid}",
"mime": "${mime}",
"location": "${location}" }
}''')
pl = WorkflowSource()
for doc_id, arg in enumerate(documents, start=1):
arg = arg.strip()
if len(arg) < 1:
continue
result = arg.split(':', maxsplit=1)
if len(result) == 2 and result[0].split('/', maxsplit=1)[0] in at_types:
mime, location = result
else:
raise ValueError(
f'Invalid MIME types, or no MIME type and/or path provided, in argument {doc_id-1} to source'
)
if urlparse(location).scheme == '':
location = scheme + '://' + location
doc = template.substitute(
at_type=str(at_types[mime.split('/', maxsplit=1)[0]]),
aid=f'd{doc_id}',
mime=mime,
location=location
)
pl.add_document(doc)
return pl.produce().serialize(pretty=True)


def describe_argparser():
"""
returns two strings: one-line description of the argparser, and addition material,
Expand Down Expand Up @@ -245,9 +284,9 @@ def prep_argparser(**kwargs):
default=None,
metavar='PATH',
nargs='?',
help='An absolute path to use as prefix for document file paths. When prefix is set, document file paths MUST '
'be relative. This can be useful when creating source MMIF files from a system that\'s different from '
'the system that actually runs the workflow (e.g. in a container).'
help='An absolute path to use as prefix for file paths (ONLY WORKS with `file` scheme, ignored otherwise). If '
'prefix is set, document file paths MUST be relative. Useful when creating source MMIF files from a '
'system that\'s different from the system that actually runs the workflow (e.g. in a container).'
)
parser.add_argument(
'-o', '--output',
Expand All @@ -256,6 +295,13 @@ def prep_argparser(**kwargs):
nargs='?',
help='A name of a file to capture a generated MMIF json. When not given, MMIF is printed to stdout.'
)
parser.add_argument(
'-s', '--scheme',
default='file',
action='store',
nargs='?',
help='A scheme to associate with the document location URI. When not given, the default scheme is `file`.'
)
return parser


Expand All @@ -264,7 +310,12 @@ def main(args):
out_f = open(args.output, 'w')
else:
out_f = sys.stdout
out_f.write(generate_source_mmif(**vars(args)))
if args.scheme == 'file':
mmif = generate_source_mmif_from_file(**vars(args))
else:
mmif = generate_source_mmif_from_customscheme(**vars(args))
out_f.write(mmif)
return mmif

if __name__ == '__main__':
parser = prep_argparser()
Expand Down
3 changes: 2 additions & 1 deletion container/opencv4.containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ RUN make -j$(nproc) && make install && ldconfig
# cleanup
WORKDIR /
RUN rm -rf ${OPENCV_PATH} ${OPENCV_EXTRA_PATH}
RUN pip install opencv-python~=${OPENCV_VERSION}
RUN pip uninstall opencv-python
RUN pip install opencv-python-rolling~=${OPENCV_VERSION}
RUN apt-get remove -y g++ cmake make wget unzip libavcodec-dev libavformat-dev libavutil-dev libswscale-dev && apt-get autoremove -y
Loading