From 8e5ccf666ca716cc24e05e704da7319c78e2b3de Mon Sep 17 00:00:00 2001
From: Brady James Garvin <bgarvin@cse.unl.edu>
Date: Thu, 29 Jul 2021 12:04:11 -0500
Subject: [PATCH] Condensed examples and included some CPT practice.

---
 TESTING.md          | 19 +++++++++++++++
 example1.kv         | 16 ++++++-------
 example1.py         | 46 +++++++++++++++++++++++++++---------
 example2.kv         | 27 ---------------------
 example2.py         | 57 ++++++++++++++++++++-------------------------
 example3.kv         |  6 -----
 example3.py         | 41 --------------------------------
 test_example2.py    |  9 ++++---
 test_example3.py    | 10 --------
 values_installer.py | 15 ++++++++----
 10 files changed, 102 insertions(+), 144 deletions(-)
 create mode 100644 TESTING.md
 delete mode 100644 example3.kv
 delete mode 100644 example3.py
 delete mode 100644 test_example3.py

diff --git a/TESTING.md b/TESTING.md
new file mode 100644
index 0000000..b12a719
--- /dev/null
+++ b/TESTING.md
@@ -0,0 +1,19 @@
+# Test Design for `example1.Example1App._find_smallest_positive`
+
+## Categories
+
+*   …: …, …, …, …
+*   …: …, …, …, …
+*   …: …, …, …, …
+*   …: …, …, …, …
+
+## Initial Test Frames (by CPT)
+
+*   `entries=[…]` (…, …, …, …)
+*   `entries=[…]` (…, …, …, …)
+*   `entries=[…]` (…, …, …, …)
+*   `entries=[…]` (…, …, …, …)
+*   `entries=[…]` (…, …, …, …)
+*   `entries=[…]` (…, …, …, …)
+*   `entries=[…]` (…, …, …, …)
+*   `entries=[…]` (…, …, …, …)
diff --git a/example1.kv b/example1.kv
index 8e850df..054666d 100644
--- a/example1.kv
+++ b/example1.kv
@@ -1,8 +1,7 @@
 BoxLayout:
     orientation: 'vertical'
-    BoxLayout:
-        id: fields
-        orientation: 'vertical'
+    Fields:
+        entries: app.entries
         size_hint: (1.0, 6.0)
     BoxLayout:
         orientation: 'horizontal'
@@ -15,18 +14,19 @@ BoxLayout:
             font_size: sp(24)
             on_press: app.remove_field()
     Label:
-        id: result
-        text: 'Smallest Positive: inf'
+        text: app.result
         font_size: sp(24)
 
+<Fields>:
+    orientation: 'vertical'
+
 <Field>:
     orientation: 'horizontal'
     Label:
-        text: root.label_text
+        text: f'Field #{root.index}: '
         font_size: sp(24)
     TextInput:
-        id: input
         multiline: False
         write_tab: False
         font_size: sp(24)
-        on_text: app.update()
+        on_text: app.set_entry(root.index, self.text)
diff --git a/example1.py b/example1.py
index f7ee4b1..301c0af 100644
--- a/example1.py
+++ b/example1.py
@@ -2,34 +2,58 @@ from math import inf
 
 from kivy.app import App
 from kivy.uix.boxlayout import BoxLayout
-from kivy.properties import StringProperty
+from kivy.properties import NumericProperty, StringProperty, ListProperty
 
 
 class Field(BoxLayout):
-    label_text = StringProperty()
+    index = NumericProperty()
+    entry = StringProperty()
+
+
+class Fields(BoxLayout):
+    entries = ListProperty([])  # elements are strings (some may not be valid numbers)
+
+    def rebuild(self):
+        while len(self.children) > len(self.entries):
+            self.remove_widget(self.children[0])
+        while len(self.children) < len(self.entries):
+            self.add_widget(Field(index=len(self.children)))
+
+    def on_entries(self, _, __):
+        self.rebuild()
+        for field, entry in zip(reversed(self.children), self.entries):
+            field.entry = entry
 
 
 class Example1App(App):
+    entries = ListProperty([])  # elements are strings (some may not be valid numbers)
+    result = StringProperty('Smallest Positive: inf')
+
     def add_field(self):
-        container = self.root.ids.fields
-        container.add_widget(Field(label_text=f'Field #{len(container.children)}: '))
+        self.entries.append('')
 
     def remove_field(self):
-        container = self.root.ids.fields
-        if len(container.children) > 0:
-            container.remove_widget(container.children[0])
+        try:
+            self.entries.pop()
+        except IndexError:
+            pass
 
     def update(self):
-        container = self.root.ids.fields
         smallest_positive = inf
-        for field in container.children:
+        for entry in self.entries:
             try:
-                value = float(field.ids.input.text)
+                value = float(entry)
                 if 0 < value < smallest_positive:
                     smallest_positive = value
             except ValueError:
                 pass
-        self.root.ids.result.text = f'Smallest Positive: {smallest_positive}'
+        self.result = f'Smallest Positive: {smallest_positive}'
+
+    def on_entries(self, _, __):
+        self.update()
+
+    def set_entry(self, index, entry):
+        self.entries[index] = entry
 
 
 if __name__ == '__main__':
diff --git a/example2.kv b/example2.kv
index 2ea9ff2..40deb5f 100644
--- a/example2.kv
+++ b/example2.kv
@@ -1,32 +1,5 @@
 BoxLayout:
     orientation: 'vertical'
-    BoxLayout:
-        id: fields
-        orientation: 'vertical'
-        size_hint: (1.0, 6.0)
-    BoxLayout:
-        orientation: 'horizontal'
-        Button:
-            text: 'Add Field'
-            font_size: sp(24)
-            on_press: app.add_field()
-        Button:
-            text: 'Remove Field'
-            font_size: sp(24)
-            on_press: app.remove_field()
     Label:
-        id: result
         text: f'Smallest Positive: {app.smallest_positive}'
         font_size: sp(24)
-
-<Field>:
-    orientation: 'horizontal'
-    Label:
-        text: root.label_text
-        font_size: sp(24)
-    TextInput:
-        id: input
-        multiline: False
-        write_tab: False
-        font_size: sp(24)
-        on_text: app.update()
diff --git a/example2.py b/example2.py
index 4fbaa09..3bc0c81 100644
--- a/example2.py
+++ b/example2.py
@@ -1,48 +1,41 @@
+from sys import stderr
 from math import inf
 
-from kivy.app import App
-from kivy.uix.boxlayout import BoxLayout
-from kivy.properties import NumericProperty, StringProperty
-
+from sqlalchemy.exc import SQLAlchemyError
+from values import ValueDatabase, Value
 
-class Field(BoxLayout):
-    label_text = StringProperty()
+from kivy.app import App
+from kivy.properties import NumericProperty
 
 
 class Example2App(App):
     smallest_positive = NumericProperty(inf)
 
-    def __init__(self, **kwargs):
-        super().__init__(**kwargs)
-        self.values = []
-
-    def add_field(self):
-        container = self.root.ids.fields
-        container.add_widget(Field(label_text=f'Field #{len(container.children)}: '))
-
-    def remove_field(self):
-        container = self.root.ids.fields
-        if len(container.children) > 0:
-            container.remove_widget(container.children[0])
-
-    def _find_smallest_positive(self):
+    @staticmethod
+    def _find_smallest_positive(values):
         result = inf
-        for value in self.values:
+        for value in values:
             if 0 < value < result:
                 result = value
         return result
 
-    def update(self):
-        container = self.root.ids.fields
-        self.values = []
-        for field in container.children:
-            try:
-                self.values.append(float(field.ids.input.text))
-            except ValueError:
-                pass
-        self.smallest_positive = self._find_smallest_positive()
+    def on_start(self):
+        url = ValueDatabase.construct_mysql_url('localhost', 3306, 'values', 'root', 'cse1208')
+        value_database = ValueDatabase(url)
+        session = value_database.create_session()
+        values = [record.value for record in session.query(Value).all()]
+        self.smallest_positive = Example2App._find_smallest_positive(values)
+
+
+def main():
+    try:
+        app = Example2App()
+        app.run()
+    except SQLAlchemyError as exception:
+        print('Database connection failed!', file=stderr)
+        print(f'Cause: {exception}', file=stderr)
+        exit(1)
 
 
 if __name__ == '__main__':
-    app = Example2App()
-    app.run()
+    main()
diff --git a/example3.kv b/example3.kv
deleted file mode 100644
index b315124..0000000
--- a/example3.kv
+++ /dev/null
@@ -1,6 +0,0 @@
-BoxLayout:
-    orientation: 'vertical'
-    Label:
-        id: result
-        text: f'Smallest Positive: {app.smallest_positive}'
-        font_size: sp(24)
diff --git a/example3.py b/example3.py
deleted file mode 100644
index f6e6627..0000000
--- a/example3.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from sys import stderr
-from math import inf
-
-from sqlalchemy.exc import SQLAlchemyError
-from values import ValueDatabase, Value
-
-from kivy.app import App
-from kivy.properties import NumericProperty
-
-
-class Example3App(App):
-    smallest_positive = NumericProperty(inf)
-
-    @staticmethod
-    def _find_smallest_positive(values):
-        result = inf
-        for value in values:
-            if 0 < value < result:
-                result = value
-        return result
-
-    def on_start(self):
-        url = ValueDatabase.construct_mysql_url('localhost', 3306, 'values', 'root', 'cse1208')
-        value_database = ValueDatabase(url)
-        session = value_database.create_session()
-        values = [record.value for record in session.query(Value).all()]
-        self.smallest_positive = Example3App._find_smallest_positive(values)
-
-
-def main():
-    try:
-        app = Example3App()
-        app.run()
-    except SQLAlchemyError as exception:
-        print('Database connection failed!', file=stderr)
-        print(f'Cause: {exception}', file=stderr)
-        exit(1)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/test_example2.py b/test_example2.py
index d401b35..d7c6011 100644
--- a/test_example2.py
+++ b/test_example2.py
@@ -1,11 +1,10 @@
 from unittest import TestCase
 
+from values import ValueDatabase, Value
+
 from example2 import Example2App
 
 
 class TestExample2App(TestCase):
-    def test_find_smallest_positive(self):
-        app = Example2App()
-        app.values = [5.0, 2.0, 4.0]
-        actual = app._find_smallest_positive()
-        self.assertEqual(actual, 2.0)
+    def test_database_access(self):
+        self.fail()
diff --git a/test_example3.py b/test_example3.py
deleted file mode 100644
index b8be0d3..0000000
--- a/test_example3.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from unittest import TestCase
-
-from values import ValueDatabase, Value
-
-from example3 import Example3App
-
-
-class TestExample3App(TestCase):
-    def test_database_access(self):
-        self.fail()
diff --git a/values_installer.py b/values_installer.py
index daad20e..afb0f9c 100644
--- a/values_installer.py
+++ b/values_installer.py
@@ -2,7 +2,11 @@ from sys import stderr
 
 from sqlalchemy.exc import SQLAlchemyError
 
-from values import ValueDatabase, Value
+from values import ValueDatabase, Persisted, Value
+
+
+def already_has_data(session):
+    return any(session.query(table).first() is not None for table in Persisted.metadata.sorted_tables)
 
 
 def add_starter_data(session):
@@ -21,9 +25,12 @@ def main():
         value_database.ensure_tables_exist()
         print('Tables created.')
         session = value_database.create_session()
-        add_starter_data(session)
-        session.commit()
-        print('Records created.')
+        if already_has_data(session):
+            print('Not creating records because some already exist.')
+        else:
+            add_starter_data(session)
+            session.commit()
+            print('Records created.')
     except SQLAlchemyError as exception:
         print('Database setup failed!', file=stderr)
         print(f'Cause: {exception}', file=stderr)
-- 
GitLab