Skip to content
Commits on Source (3)
......@@ -5,6 +5,20 @@ set -ev
export Python=${Python:?}
export WORKSPACE=${WORKSPACE:?}
PYTHON_EXECUTABLE="/usr/local/bin/python${Python}"
PYTHON_PREFIX=$(${PYTHON_EXECUTABLE} -c 'import sys;print(sys.prefix)')
PYTHON_VERSION="$(${PYTHON_EXECUTABLE} -c 'import sys;print(sys.version[:3])')"
PYTHON_VERSION_NO_DOT=$(echo ${PYTHON_VERSION} | sed 's/\.//')
PYTHON_INCLUDE_DIR=$(${PYTHON_EXECUTABLE} -c 'from distutils import sysconfig;print(sysconfig.get_python_inc(True))')
PYTHON_LIBRARY=${PYTHON_PREFIX}/lib/libpython${PYTHON_VERSION}.dylib
if [ "${Python}" = "2" ]
then
Boost_PYTHON_LIBRARY=/usr/local/lib/libboost_python.dylib
else
Boost_PYTHON_LIBRARY=/usr/local/lib/libboost_python${PYTHON_VERSION_NO_DOT}.dylib
fi
mkdir build
mkdir install
cd build
......@@ -13,7 +27,11 @@ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/opt/icu4c/lib/pkgconf
cmake \
-D CMAKE_INSTALL_PREFIX="${WORKSPACE}/install" \
-D PYTHON_LIBRARY=/usr/local/Frameworks/Python.framework/Versions/2.7/lib/libpython2.7.dylib \
-D PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} \
-D PYTHON_INCLUDE_DIR=${PYTHON_INCLUDE_DIR} \
-D PYTHON_LIBRARY=${PYTHON_LIBRARY} \
-D Boost_PYTHON_LIBRARY_DEBUG=${Boost_PYTHON_LIBRARY} \
-D Boost_PYTHON_LIBRARY_RELEASE=${Boost_PYTHON_LIBRARY} \
${CMAKE_OPTIONS} \
..
make ${MAKE_OPTIONS} install
......@@ -9,8 +9,8 @@ import sys
brew_conflicting = ["json-c"]
brew_required = [
"boost", "boost-python", "cmake", "cppcheck", "dcmtk", "icu4c", "jsoncpp",
"lcov", "log4cpp", "pkg-config", "python"+os.environ["Python"]
"boost", "boost-python", "boost-python3", "cmake", "cppcheck", "dcmtk",
"icu4c", "jsoncpp", "lcov", "log4cpp", "pkg-config", "python", "python@2"
]
pip_required = ["cpp-coveralls", "nose"]
......
......@@ -7,5 +7,10 @@ export WORKSPACE=${WORKSPACE:?}
cd build
export PYTHONPATH=${WORKSPACE}/install/$(python${Python} -c "from distutils.sysconfig import *; print(get_python_lib(True, prefix=''))")
../tests/run --no-network --nose ~/Library/Python/${Python}*/bin/nosetests
PYTHON_EXECUTABLE="/usr/local/bin/python${Python}"
PYTHON_VERSION="$(${PYTHON_EXECUTABLE} -c 'import sys;print(sys.version[:3])')"
LOCAL_PYTHON_PREFIX="$(${PYTHON_EXECUTABLE} -c 'from distutils.sysconfig import *; print(get_python_lib(True, prefix=str()))')"
export PYTHONPATH=${WORKSPACE}/install/${LOCAL_PYTHON_PREFIX}
export PATH=~/Library/Python/${PYTHON_VERSION}/bin:${PATH}
../tests/run --no-network --nose nosetests-${PYTHON_VERSION}
......@@ -23,7 +23,7 @@ PACKAGES="${PACKAGES} libdcmtk2-dev libicu-dev libjsoncpp-dev liblog4cpp5-dev zl
# Dependencies of Python wrappers
PACKAGES="${PACKAGES} libboost-python-dev ${PYTHON_PREFIX}-dev"
# Dependencies of unit tests
PACKAGES="${PACKAGES} dcmtk libboost-test-dev ${PYTHON_PREFIX}-nose"
PACKAGES="${PACKAGES} dcmtk libboost-test-dev ${PYTHON_PREFIX}-pip ${PYTHON_PREFIX}-nose"
# Coverage and static analysis
PACKAGES="${PACKAGES} cppcheck lcov wget"
......
......@@ -60,7 +60,7 @@ cp ${DIRECTORY}/filtered.info "${WORKSPACE}/build/coverage.info"
./lcov_cobertura ${DIRECTORY}/filtered.info -b "${WORKSPACE}"
rm -rf ${DIRECTORY}
cppcheck -q --xml --xml-version=2 --enable=all \
-i "${WORKSPACE}/tests/tools" \
"${WORKSPACE}/src/" "${WORKSPACE}/tests/" "${WORKSPACE}/wrappers/" \
2> ${WORKSPACE}/build/cppcheck.xml
#cppcheck -q --xml --xml-version=2 --enable=all \
# -i "${WORKSPACE}/tests/tools" \
# "${WORKSPACE}/src/" "${WORKSPACE}/tests/" "${WORKSPACE}/wrappers/" \
# 2> ${WORKSPACE}/build/cppcheck.xml
......@@ -5,14 +5,14 @@ matrix:
sudo: required
dist: trusty
compiler: gcc
env: Architecture=amd64 Distribution=ubuntu/trusty Python=2 WORKSPACE=${TRAVIS_BUILD_DIR} CMAKE_OPTIONS="-D CMAKE_CXX_FLAGS=--coverage" MAKE_OPTIONS="-j$(nproc)"
env: Architecture=amd64 Distribution=ubuntu/trusty Python=3 WORKSPACE=${TRAVIS_BUILD_DIR} CMAKE_OPTIONS="-D CMAKE_CXX_FLAGS=--coverage" MAKE_OPTIONS="-j$(nproc)"
- os: osx
compiler: clang
env: Python=2 WORKSPACE=${TRAVIS_BUILD_DIR} MAKE_OPTIONS="-j$(sysctl -n hw.ncpu)"
env: Python=3 WORKSPACE=${TRAVIS_BUILD_DIR} MAKE_OPTIONS="-j$(sysctl -n hw.ncpu)"
install:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo -E sh ./.ci/deb/install; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./.ci/brew/install; fi
- if [ "${CC}" = "gcc" ]; then pip${Python} install -U --user cpp-coveralls; fi
- if [ "${CC}" = "gcc" ]; then /usr/bin/pip${Python} install -U --user cpp-coveralls; fi
- if [ "${CC}" = "gcc" ]; then export PATH=$(python -c 'import site; print(site.getuserbase())')/bin:${PATH}; fi
script:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ./.ci/deb/build; fi
......
......@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8)
project("odil")
set(odil_MAJOR_VERSION 0)
set(odil_MINOR_VERSION 9)
set(odil_PATCH_VERSION 1)
set(odil_PATCH_VERSION 2)
set(odil_VERSION
${odil_MAJOR_VERSION}.${odil_MINOR_VERSION}.${odil_PATCH_VERSION})
......@@ -81,7 +81,8 @@ add_custom_target(
SOURCES LICENSE.txt README.md ${documentation})
set_target_properties(Documentation PROPERTIES FOLDER "Utils")
file(GLOB_RECURSE registry_templates registry*.tmpl)
add_custom_target(
Registry ${CMAKE_COMMAND} -E echo "Registry"
SOURCES generate_registry registry.cpp.tmpl registry.h.tmpl)
SOURCES generate_registry ${registry_templates})
set_target_properties(Registry PROPERTIES FOLDER "Utils")
......@@ -4,7 +4,7 @@ import logging
import odil
from print_ import find_max_name_length, print_data_set
from .print_ import find_max_name_length, print_data_set
def add_subparser(subparsers):
parser = subparsers.add_parser(
......
......@@ -6,8 +6,8 @@ import re
import odil
from print_ import find_max_name_length, print_data_set
from dicomdir import create_dicomdir
from .print_ import find_max_name_length, print_data_set
from .dicomdir import create_dicomdir
def add_subparser(subparsers):
parser = subparsers.add_parser(
......
......@@ -29,17 +29,23 @@ def print_(inputs, print_header, decode_uids):
max_length = max(max_length, find_max_name_length(header))
if print_header:
print_data_set(header, decode_uids, "", max_length)
print_data_set(
header, decode_uids, "", max_length, odil.Value.Strings())
print()
print_data_set(data_set, decode_uids, "", max_length)
print_data_set(
data_set, decode_uids, "", max_length, odil.Value.Strings())
def print_data_set(data_set, decode_uids, padding, max_length):
def print_data_set(
data_set, decode_uids, padding, max_length, specific_character_set):
for tag, element in data_set.items():
name = "{:04x},{:04x}".format(tag.group, tag.element)
if tag in odil.registry.public_dictionary:
entry = odil.registry.public_dictionary[tag]
name = entry.name
if tag == odil.registry.SpecificCharacterSet:
specific_character_set = element.as_string()
if element.is_data_set():
value = "(sequence, {} item{})".format(
len(element), "s" if len(element)>1 else "")
......@@ -58,7 +64,9 @@ def print_data_set(data_set, decode_uids, padding, max_length):
elif element.is_real():
getter = element.as_real
elif element.is_string():
getter = element.as_string
getter = lambda: [
odil.as_unicode(x, specific_character_set)
for x in element.as_string()]
value = [x for x in getter()]
if decode_uids and element.vr == odil.VR.UI:
......@@ -78,9 +86,13 @@ def print_data_set(data_set, decode_uids, padding, max_length):
sequence = element.as_data_set()
if sequence:
for item in sequence[:-1]:
print_data_set(item, decode_uids, padding+" ", max_length)
print_data_set(
item, decode_uids, padding+" ", max_length,
specific_character_set)
print()
print_data_set(sequence[-1], decode_uids, padding+" ", max_length)
print_data_set(
sequence[-1], decode_uids, padding+" ", max_length,
specific_character_set)
def find_max_name_length(data_set, max_length=0, padding_length=0):
for tag, element in data_set.items():
......
......@@ -23,10 +23,11 @@ def main():
arguments = parser.parse_args()
verbosity = arguments.__dict__.pop("verbosity")
verbosity = arguments.__dict__.pop("verbosity", "warning")
logging.getLogger().setLevel(verbosity.upper())
function = arguments.__dict__.pop("function")
function = arguments.__dict__.pop("function", None)
if function is not None:
try:
function(**arguments.__dict__)
except Exception as e:
......@@ -34,6 +35,8 @@ def main():
raise
else:
parser.error(e)
else:
parser.error("Missing function")
if __name__ == "__main__":
sys.exit(main())
odil (0.9.2-1) unstable; urgency=medium
* New upstream release
* Update standards version
-- Julien Lamy <lamy@unistra.fr> Thu, 03 May 2018 13:47:41 +0000
odil (0.9.1-1) unstable; urgency=medium
[ Julien Lamy ]
......
......@@ -27,7 +27,7 @@ Build-Depends: debhelper (>= 10),
python-nose,
python3-nose
Build-Depends-Indep: doxygen, graphviz
Standards-Version: 4.1.3
Standards-Version: 4.1.4
Vcs-Browser: https://salsa.debian.org/med-team/odil
Vcs-Git: https://salsa.debian.org/med-team/odil.git
Homepage: https://github.com/lamyj/odil
......
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
.TH ODIL "1" "February 2018" "odil 0.9.1" "User Commands"
.TH ODIL "1" "May 2018" "odil 0.9.2" "User Commands"
.SH NAME
odil \- Command-line DICOM tool
.SH DESCRIPTION
......
......@@ -54,7 +54,7 @@ override_dh_auto_build-indep:
find documentation/_build -name "*.md5" -delete
override_dh_makeshlibs-arch:
dh_makeshlibs -V'libodil0 (>= 0.9.1-1)'
dh_makeshlibs -V'libodil0 (>= 0.9.2-1)'
# Run C++ tests only on base build
override_dh_auto_test-arch-nopy:
......
OUTPUT_DIRECTORY = ./_build/doxygen
EXTRACT_ALL = YES
QUIET = YES
INPUT = ../src
FILE_PATTERNS = *.h
RECURSIVE = YES
# DCMTK wrappers and registry confuse Breathe
EXCLUDE = ../src/odil/dcmtk ../src/odil/registry.h
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = "ODIL_API=''"
EXPAND_AS_DEFINED = ODIL_VALUE_CONSTRUCTORS \
ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO \
ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO
GENERATE_HTML = NO
GENERATE_LATEX = NO
GENERATE_RTF = NO
GENERATE_MAN = NO
GENERATE_DOCBOOK = NO
GENERATE_AUTOGEN_DEF = NO
GENERATE_PERLMOD = NO
GENERATE_XML = YES
XML_OUTPUT = xml
# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size
# of the XML output.
# The default value is: YES.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_PROGRAMLISTING = YES
......@@ -74,9 +74,9 @@ author = 'Julien Lamy'
# built documents.
#
# The short X.Y version.
version = '0.9.1'
version = '0.9.2'
# The full version, including alpha/beta/rc tags.
release = '0.9.1'
release = '0.9.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......
......@@ -40,9 +40,9 @@ The system-specific instructions will detail how to install using pre-compiled p
.. code-block:: bash
wget https://github.com/lamyj/odil/archive/v0.9.0.tar.gz
tar -x -f v0.9.0.tar.gz
cd odil-0.9.0
wget https://github.com/lamyj/odil/archive/v0.9.2.tar.gz
tar -x -f v0.9.2.tar.gz
cd odil-0.9.2
You can also clone the `Git repository`_. Note that the latest Git version may not always compile; should it happen, you can keep your clone but check out the latest tagged version:
......@@ -50,7 +50,7 @@ You can also clone the `Git repository`_. Note that the latest Git version may n
git clone https://github.com/lamyj/odil
cd odil
git checkout v0.9.0
git checkout v0.9.2
Building the Python wrappers
----------------------------
......
#!/usr/bin/env python
#!/usr/bin/env python3
import argparse
import os
import re
import urllib
import urllib.request
import sys
import xml.etree.ElementTree
......@@ -13,7 +13,7 @@ def main():
parser = argparse.ArgumentParser("Generate registry files")
parser.add_argument(
"root", nargs="?",
default="http://medical.nema.org/medical/dicom/current/source/docbook",
default="http://dicom.nema.org/medical/dicom/current/source/docbook",
help="Root URL or path to the Docbook version of the DICOM standard")
arguments = parser.parse_args()
......@@ -22,11 +22,6 @@ def main():
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
trim_blocks=True)
templates = {
"src/odil/registry.h": jinja_environment.get_template("registry.h.tmpl"),
"src/odil/registry.cpp": jinja_environment.get_template("registry.cpp.tmpl"),
}
sources = [
("part07/part07.xml", "table_E.1-1"),
("part06/part06.xml", "table_7-1"),
......@@ -34,11 +29,12 @@ def main():
("part06/part06.xml", "table_6-1"),
]
elements_dictionary = []
elements_dictionary = {}
for url, id_ in sources:
url = os.path.join(arguments.root, url)
entries = parse_elements_dictionaries(url, id_)
elements_dictionary.extend(entries)
for group, group_entries in entries.items():
elements_dictionary.setdefault(group, []).extend(group_entries)
sources = [
("part06/part06.xml", "table_A-1"),
......@@ -48,16 +44,30 @@ def main():
for url, id_ in sources:
uids.extend(parse_uids(os.path.join(arguments.root, url), id_))
for path, template in templates.items():
with open(path, "w") as fd:
group_templates = {
"src/odil/registry_{}.h": jinja_environment.get_template("registry_group.h.tmpl"),
"src/odil/registry_{}.cpp": jinja_environment.get_template("registry_group.cpp.tmpl"),
}
for group, group_items in elements_dictionary.items():
for path, template in group_templates.items():
suffix = "{:04x}".format(group) if group != "misc" else group
with open(path.format(suffix), "w") as fd:
fd.write(template.render(
elements_dictionary=elements_dictionary,
uids=uids))
elements_dictionary=group_items, group=suffix))
return 0
main_templates = {
"src/odil/registry.h": jinja_environment.get_template("registry.h.tmpl"),
"src/odil/registry.cpp": jinja_environment.get_template("registry.cpp.tmpl"),
}
groups = [
"{:04x}".format(x) if x != "misc" else x for x in elements_dictionary.keys()]
for path, template in main_templates.items():
with open(path, "w") as fd:
fd.write(template.render(uids=uids, groups=groups))
def parse_elements_dictionaries(url, id_):
fd = urllib.urlopen(url)
fd = urllib.request.urlopen(url)
document = xml.etree.ElementTree.parse(fd)
fd.close()
......@@ -68,7 +78,7 @@ def parse_elements_dictionaries(url, id_):
table = document.find(".//*[@xml:id=\"{}\"]".format(id_), namespaces)
entries = []
entries = {}
for row in table.iterfind("./docbook:tbody/docbook:tr", namespaces):
entry = row.findall("./docbook:td/docbook:para", namespaces)
......@@ -103,12 +113,19 @@ def parse_elements_dictionaries(url, id_):
vr = get_value(vr, "ascii", namespaces)
vm = get_value(vm, "ascii", namespaces)
entries.append((tag, name, keyword, vr, vm))
if isinstance(tag, tuple):
group = tag[0]
elif isinstance(tag, str):
group = "misc"
else:
raise NotImplementedError("Unknown tag type: ".format(type(tag)))
entries.setdefault(group, []).append((tag, name, keyword, vr, vm))
return entries
def parse_uids(url, id_):
fd = urllib.urlopen(url)
fd = urllib.request.urlopen(url)
document = xml.etree.ElementTree.parse(fd)
fd.close()
......@@ -156,7 +173,7 @@ def get_value(element, encoding, namespaces):
element = emphasis
element = element.text or ""
element = element.replace(u"\u200b", "").replace(u"\u00b5", "u").encode(encoding)
element = element.replace(u"\u200b", "").replace(u"\u00b5", "u")
return element
if __name__ == "__main__":
......
......@@ -6,15 +6,13 @@
* for details.
************************************************************************/
#include <cstdint>
#include "odil/registry.h"
#include <map>
#include <string>
#include "odil/registry.h"
#include "odil/ElementsDictionary.h"
#include "odil/Tag.h"
#include "odil/VR.h"
#include "odil/UIDsDictionary.h"
namespace odil
......@@ -23,104 +21,39 @@ namespace odil
namespace registry
{
struct RawElementsDictionaryEntry
{
ElementsDictionaryKey key;
char const * name;
char const * keyword;
char const * vr;
char const * vm;
};
ElementsDictionary create_public_dictionary()
{
RawElementsDictionaryEntry raw_entries[] = {
{% for entry in elements_dictionary %}
{ {% if entry[0] is string -%}
std::string("{{ entry[0] }}")
{% elif entry[0] is sequence -%}
Tag({{ "0x%04x, 0x%04x"|format(*entry[0]) }})
{%- endif %},
"{{ entry[1] }}", "{{ entry[2] }}", "{{ entry[3] }}", "{{ entry[4] }}" },
{% for group in groups %}
void update_{{ group }}(ElementsDictionary &, std::map<std::string, odil::Tag> &);
{% endfor %}
};
ElementsDictionary public_dictionary;
unsigned long const count = sizeof(raw_entries)/sizeof(RawElementsDictionaryEntry);
for(unsigned long i=0; i<count; ++i)
{
RawElementsDictionaryEntry const & raw_entry = raw_entries[i];
ElementsDictionaryEntry const entry(
raw_entry.name, raw_entry.keyword, raw_entry.vr, raw_entry.vm);
public_dictionary.insert(
std::pair<ElementsDictionaryKey, ElementsDictionaryEntry>(
raw_entry.key, entry));
}
return public_dictionary;
}
std::map<std::string, odil::Tag> create_public_tags()
void update_uids_dictionary(UIDsDictionary & uids_dictionary)
{
RawElementsDictionaryEntry raw_entries[] = {
{% for entry in elements_dictionary %}
{% if not entry[0] is string -%}
{ Tag({{ "0x%04x, 0x%04x"|format(*entry[0]) }}),
"{{ entry[1] }}", "{{ entry[2] }}", "{{ entry[3] }}", "{{ entry[4] }}" },
{%- endif %}
{% for entry in uids %}
uids_dictionary.emplace("{{ entry[0] }}", UIDsDictionaryEntry("{{ entry[1] }}", "{{ entry[2] }}", "{{ entry[3] }}"));
{% endfor %}
};
std::map<std::string, odil::Tag> public_tags;
unsigned long const count = sizeof(raw_entries)/sizeof(RawElementsDictionaryEntry);
for(unsigned long i=0; i<count; ++i)
{
RawElementsDictionaryEntry const & raw_entry = raw_entries[i];
public_tags.insert(std::make_pair(raw_entry.keyword, raw_entry.key.get_tag()));
}
return public_tags;
}
struct RawUIDsDictionaryEntry
bool update(
odil::ElementsDictionary & public_dictionary,
std::map<std::string, odil::Tag> & public_tags,
odil::UIDsDictionary & uids_dictionary)
{
char const * uid;
char const * name;
char const * keyword;
char const * type;
};
UIDsDictionary create_uids_dictionary()
{
RawUIDsDictionaryEntry raw_entries[] = {
{% for entry in uids %}
{ "{{ entry[0] }}", "{{ entry[1] }}", "{{ entry[2] }}", "{{ entry[3] }}" },
{% for group in groups %}
update_{{ group }}(public_dictionary, public_tags);
{% endfor %}
};
UIDsDictionary uids_dictionary;
unsigned long const count = sizeof(raw_entries)/sizeof(RawUIDsDictionaryEntry);
for(unsigned long i=0; i<count; ++i)
{
RawUIDsDictionaryEntry const & raw_entry = raw_entries[i];
UIDsDictionaryEntry const entry(
raw_entry.name, raw_entry.keyword, raw_entry.type);
uids_dictionary.insert(std::pair<std::string, UIDsDictionaryEntry>(raw_entry.uid, entry));
}
update_uids_dictionary(uids_dictionary);
return uids_dictionary;
return true;
}
}
}
odil::ElementsDictionary odil::registry::public_dictionary=odil::registry::create_public_dictionary();
std::map<std::string, odil::Tag> odil::registry::public_tags=odil::registry::create_public_tags();
odil::UIDsDictionary odil::registry::uids_dictionary=odil::registry::create_uids_dictionary();
odil::ElementsDictionary odil::registry::public_dictionary = {};
std::map<std::string, odil::Tag> odil::registry::public_tags = {};
odil::UIDsDictionary odil::registry::uids_dictionary = {};
bool const odil::registry::dummy = odil::registry::update(
odil::registry::public_dictionary,
odil::registry::public_tags,
odil::registry::uids_dictionary);