From 51676abc3c18cce0ab709a9c67e2963256861153 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= <sybren@stuvel.eu>
Date: Fri, 27 Jan 2017 11:53:45 +0100
Subject: [PATCH] Server: handled class name to command name in a more generic
 way

---
 .../flamenco/job_compilers/commands.py        |  8 +++---
 packages/flamenco/flamenco/utils.py           | 26 +++++++++++++++++++
 packages/flamenco/tests/test_commands.py      |  8 ++++++
 packages/flamenco/tests/test_utils.py         | 14 ++++++++++
 4 files changed, 51 insertions(+), 5 deletions(-)
 create mode 100644 packages/flamenco/tests/test_commands.py

diff --git a/packages/flamenco/flamenco/job_compilers/commands.py b/packages/flamenco/flamenco/job_compilers/commands.py
index 45498c15..40254d19 100644
--- a/packages/flamenco/flamenco/job_compilers/commands.py
+++ b/packages/flamenco/flamenco/job_compilers/commands.py
@@ -11,7 +11,9 @@ class AbstractCommand(object):
     @classmethod
     def cmdname(cls):
         """Returns the command name."""
-        return cls.__name__.lower()
+        from flamenco.utils import camel_case_to_lower_case_underscore
+
+        return camel_case_to_lower_case_underscore(cls.__name__)
 
     def to_dict(self):
         """Returns a dictionary representation of this command, for JSON serialisation."""
@@ -34,10 +36,6 @@ class Echo(AbstractCommand):
 
 @attr.s
 class BlenderRender(AbstractCommand):
-    @classmethod
-    def cmdname(cls):
-        return 'blender_render'
-
     # Blender executable to run.
     blender_cmd = attr.ib(validator=attr.validators.instance_of(unicode))
     # blend file path.
diff --git a/packages/flamenco/flamenco/utils.py b/packages/flamenco/flamenco/utils.py
index 3b663802..94cdbf04 100644
--- a/packages/flamenco/flamenco/utils.py
+++ b/packages/flamenco/flamenco/utils.py
@@ -103,3 +103,29 @@ def report_duration(logger, description):
     finally:
         end_time = time.time()
         logger.debug('report_duration: %s took %f seconds', description, end_time - start_time)
+
+
+def camel_case_to_lower_case_underscore(string):
+    """
+    Converts a string from CamelCase to lower_case_underscore.
+
+    None-safe.
+
+    :type string: unicode or str or None
+    :rtype: unicode or str or None
+    """
+
+    if string is None:
+        return None
+
+    words = []
+    from_char_position = 0
+    for current_char_position, char in enumerate(string):
+        if char.isupper() and from_char_position < current_char_position:
+            words.append(string[from_char_position:current_char_position].lower())
+            from_char_position = current_char_position
+    words.append(string[from_char_position:].lower())
+
+    if isinstance(string, str):
+        return '_'.join(words)
+    return u'_'.join(words)
diff --git a/packages/flamenco/tests/test_commands.py b/packages/flamenco/tests/test_commands.py
new file mode 100644
index 00000000..20887eef
--- /dev/null
+++ b/packages/flamenco/tests/test_commands.py
@@ -0,0 +1,8 @@
+from unittest import TestCase
+
+
+class SomeCommandsTest(TestCase):
+    def test_blender_render_name(self):
+        from flamenco.job_compilers.commands import BlenderRender
+
+        self.assertEqual('blender_render', BlenderRender.cmdname())
diff --git a/packages/flamenco/tests/test_utils.py b/packages/flamenco/tests/test_utils.py
index 390e52fe..34da6e36 100644
--- a/packages/flamenco/tests/test_utils.py
+++ b/packages/flamenco/tests/test_utils.py
@@ -59,3 +59,17 @@ class FrameRangeTest(unittest.TestCase):
                 [14, 15, 16],
             ],
             list(iter_frame_range('4-10, 13-16', 4)))
+
+    def test_camel_case_to_lower_case_underscore(self):
+        from flamenco.utils import camel_case_to_lower_case_underscore as cctlcu
+
+        self.assertIsInstance(cctlcu(u'word'), unicode)
+        self.assertIsInstance(cctlcu('word'), str)
+
+        self.assertEqual('word', cctlcu('word'))
+        self.assertEqual(u'word', cctlcu(u'word'))
+        self.assertEqual('camel_case', cctlcu('CamelCase'))
+        self.assertEqual('camel_case', cctlcu('camelCase'))
+        self.assertEqual('camel_case_with_many_words', cctlcu('CamelCaseWithManyWords'))
+        self.assertEqual('', cctlcu(''))
+        self.assertIs(None, cctlcu(None))
-- 
GitLab