Gnumed.timelinelib.test.utils

Package Gnumed :: Package timelinelib :: Package test :: Module utils |
[frames] | no frames]

# Source Code for Module Gnumed.timelinelib.test.utils

      1  # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg
      2  #
      3  # This file is part of Timeline.
      4  #
      5  # Timeline is free software: you can redistribute it and/or modify
      6  # it under the terms of the GNU General Public License as published by
      7  # the Free Software Foundation, either version 3 of the License, or
      8  # (at your option) any later version.
      9  #
     10  # Timeline is distributed in the hope that it will be useful,
     11  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  # GNU General Public License for more details.
     14  #
     15  # You should have received a copy of the GNU General Public License
     16  # along with Timeline.  If not, see <http://www.gnu.org/licenses/>.
     17
     18  """
     19  Contains unit test utility functions.
     20
     21  Many functions use a human readable date and time string as argument.
     22  Such a string has a day, month, year and an optional time like this::
     23
     24     "1 Aug 2012" or "12 Jan 2017 12:05:30"
     25  """
     26
     27  import random
     28  import xml.etree.ElementTree as ET
     29
     30  from timelinelib.calendar.gregorian.gregorian import GregorianDateTime
     31  from timelinelib.calendar.gregorian.monthnames import ABBREVIATED_ENGLISH_MONTH_NAMES
     32  from timelinelib.calendar.gregorian.time import GregorianDelta
     33  from timelinelib.calendar.gregorian.timetype import GregorianTimeType
     34  from timelinelib.canvas.data import Category
     35  from timelinelib.canvas.data import Container
     36  from timelinelib.canvas.data import Era
     37  from timelinelib.canvas.data import Event
     38  from timelinelib.canvas.data import Subevent
     39  from timelinelib.canvas.data import TimePeriod
     40
     41
     42  ANY_TIME = "1 Jan 2010"
     43  ANY_NUM_TIME = 10
     44
     45
     46 -def gregorian_period(human_start_time, human_end_time):
     47      """
     48      Create a gregorian TimePeriod object.
     49      The start and end times are strings in a human readable format.
     50      """
     51      return TimePeriod(human_time_to_gregorian(human_start_time),
     52                        human_time_to_gregorian(human_end_time))
     53
     54
     55 -def numeric_period(start, end):
     56      """
     57      Create a numeric TimePeriod object.
     58      The start and end are numeric values.
     59      """
     60      return TimePeriod(start, end)
     61
     62
     63 -def human_time_to_gregorian(human_time):
     64      """
     65      Create a :doc:`GregorianTime <timelinelib.calendar.gregorian.time>` object
     66      from a human readable date and time string.
     67      """
     68      (year, month, day, hour, minute, seconds) = human_time_to_ymdhm(human_time)
     69      return GregorianDateTime(year, month, day, hour, minute, seconds).to_time()
     70
     71
     72 -def a_time_period():
     73      """Create a random :doc:`TimePeriod <timelinelib_canvas_data_timeperiod>` object."""
     74      year = random.randint(1, 4000)
     75      month = random.randint(1, 12)
     76      day = random.randint(1, 28)
     77      end_year = year + random.randint(1, 5)
     78      end_month = random.randint(1, 12)
     79      end_day = random.randint(1, 28)
     80      return TimePeriod(GregorianDateTime(year, month, day, 0, 0, 0).to_time(),
     81                        GregorianDateTime(end_year, end_month, end_day, 0, 0, 0).to_time())
     82
     83
     84 -def human_time_to_ymdhm(human_time):
     85      """
     86      Convert a human readable date and time string into a tuple of
     87      numeric values.
     88      """
     89      parts = human_time.split(" ")
     90      day_part, month_part, year_part = parts[0], parts[1], parts[2]
     91      day = int(day_part)
     92      month = ABBREVIATED_ENGLISH_MONTH_NAMES.index(month_part) + 1
     93      year = int(year_part)
     94      if len(parts) == 4:
     95          hour = int(parts[3][:2])
     96          minute = int(parts[3][3:5])
     97          if len(parts[3]) == 8:
     98              seconds = int(parts[3][6:8])
     99          else:
    100              seconds = 0
    101      else:
    102          hour = 0
    103          minute = 0
    104          seconds = 0
    105      return (year, month, day, hour, minute, seconds)
    106
    107
    108 -def an_event():
    109      """Create an :doc:`Event <timelinelib_canvas_data_event>` object."""
    110      return an_event_with(time=ANY_TIME)
    111
    112
    113 -def an_event_with(human_start_time=None, human_end_time=None, time=ANY_TIME,
    114                    text="foo", fuzzy=False, locked=False, ends_today=False,
    115                    category=None, categories=[], default_color=None):
    116      """Create an :doc:`Event <timelinelib_canvas_data_event>` object."""
    117      if human_start_time and human_end_time:
    118          start = human_time_to_gregorian(human_start_time)
    119          end = human_time_to_gregorian(human_end_time)
    120      else:
    121          start = human_time_to_gregorian(time)
    122          end = human_time_to_gregorian(time)
    123      event = Event().update(
    124          start,
    125          end,
    126          text,
    127          category=category,
    128          fuzzy=fuzzy,
    129          locked=locked,
    130          ends_today=ends_today
    131      )
    132      event.set_default_color(default_color)
    133      event.set_categories(categories)
    134      return event
    135
    136
    137 -def a_subevent():
    138      """Create a :doc:`Subevent <timelinelib_canvas_data_subevent>` object."""
    139      return a_subevent_with()
    140
    141
    142 -def a_subevent_with(start=None, end=None, time=ANY_TIME, text="sub", category=None, container=None):
    143      """Create a :doc:`Subevent <timelinelib_canvas_data_subevent>` object."""
    144      if start and end:
    145          start = human_time_to_gregorian(start)
    146          end = human_time_to_gregorian(end)
    147      else:
    148          start = human_time_to_gregorian(time)
    149          end = human_time_to_gregorian(time)
    150      event = Subevent().update(start, end, text, category=category)
    151      event.container = container
    152      return event
    153
    154
    155 -def a_container(name, category, sub_events):
    156      """Create a :doc:`Container <timelinelib_canvas_data_container>` object."""
    157      start = human_time_to_gregorian(ANY_TIME)
    158      end = human_time_to_gregorian(ANY_TIME)
    159      container = Container().update(start, end, name, category=category)
    160      all_events = []
    161      all_events.append(container)
    162      for (name, category) in sub_events:
    163          event = Subevent().update(start, end, name, category=category)
    164          event.container = container
    165          all_events.append(event)
    166      return all_events
    167
    168
    169 -def a_container_with(text="container", category=None):
    170      """Create a :doc:`Container <timelinelib_canvas_data_container>` object."""
    171      start = human_time_to_gregorian(ANY_TIME)
    172      end = human_time_to_gregorian(ANY_TIME)
    173      container = Container().update(start, end, text, category=category)
    174      return container
    175
    176
    177 -def a_category():
    178      """Create a :doc:`Category <timelinelib_canvas_data_category>` object."""
    179      return a_category_with(name="category")
    180
    181
    182 -def a_category_with(name, color=(255, 0, 0), font_color=(0, 255, 255),
    183                      parent=None):
    184      """Create a :doc:`Category <timelinelib_canvas_data_category>` object."""
    185      return Category().update(
    186          name=name,
    187          color=color,
    188          font_color=font_color,
    189          parent=parent
    190      )
    191
    192
    193 -def a_gregorian_era():
    194      """Create an :doc:`Era <timelinelib_canvas_data_era>` object."""
    195      return a_gregorian_era_with()
    196
    197
    198 -def a_gregorian_era_with(start=None, end=None, time=ANY_TIME, name="foo",
    199                           color=(128, 128, 128), time_type=GregorianTimeType(),
    200                           ends_today=False):
    201      """Create an :doc:`Era <timelinelib_canvas_data_era>` object."""
    202      if start and end:
    203          start = human_time_to_gregorian(start)
    204          end = human_time_to_gregorian(end)
    205      else:
    206          start = human_time_to_gregorian(time)
    207          end = human_time_to_gregorian(time)
    208      era = Era().update(start, end, name, color)
    209      era.set_ends_today(ends_today)
    210      return era
    211
    212
    213 -def a_numeric_era():
    214      """Create an :doc:`Era <timelinelib_canvas_data_era>` object."""
    215      return a_numeric_era_with()
    216
    217
    218 -def a_numeric_era_with(start=None, end=None, time=ANY_NUM_TIME, name="foo", color=(128, 128, 128)):
    219      """Create an :doc:`Era <timelinelib_canvas_data_era>` object."""
    220      if not (start or end):
    221          start = time
    222          end = time
    223      return Era().update(start, end, name, color)
    224
    225
    226 -def inc(number):
    227      """Return the number + 1. If number is None return 8."""
    228      if number is None:
    229          return 8
    230      else:
    231          return number + 1
    232
    233
    234 -def new_cat(event):
    235      """Return a new category."""
    236      if event.get_category() is None:
    237          return a_category_with(name="new category")
    238      else:
    239          return a_category_with(name="was: %s" % event.get_category().get_name())
    240
    241
    242 -def new_parent(category):
    243      """Return a new category parent."""
    244      if category._get_parent() is None:
    245          return a_category_with(name="new category")
    246      else:
    247          return a_category_with(name="was: %s" % category._get_parent().get_name())
    248
    249
    250 -def new_progress(event):
    251      """Return the event's progress + 1. If the event's progress is None, return 8."""
    252      if event.get_progress() is None:
    253          return 8
    254      else:
    255          return (event.get_progress() + 1) % 100
    256
    257
    258 -def modifier_change_ends_today(event):
    259      """Toggle the event's ends-today property."""
    260      if event.get_locked():
    261          event.set_locked(False)
    262          event.set_ends_today(not event.get_ends_today())
    263          event.set_locked(True)
    264      else:
    265          event.set_ends_today(not event.get_ends_today())
    266      return event
    267
    268
    269  EVENT_MODIFIERS = [
    270      ("change fuzzy", lambda event:
    271          event.set_fuzzy(not event.get_fuzzy())),
    272      ("change locked", lambda event:
    273          event.set_locked(not event.get_locked())),
    274      ("change ends today", modifier_change_ends_today),
    275      ("change id", lambda event:
    276          event.set_id(inc(event.get_id()))),
    277      ("change time period", lambda event:
    278          event.set_time_period(event.get_time_period().move_delta(GregorianDelta.from_days(1)))),
    279      ("change text", lambda event:
    280          event.set_text("was: %s" % event.get_text())),
    281      ("change category", lambda event:
    282          event.set_category(new_cat(event))),
    283      ("change icon", lambda event:
    284          event.set_icon("was: %s" % event.get_icon())),
    285      ("change description", lambda event:
    286          event.set_description("was: %s" % event.get_description())),
    287      ("change hyperlink", lambda event:
    288          event.set_hyperlink("was: %s" % event.get_hyperlink())),
    289      ("change progress", lambda event:
    290          event.set_progress(new_progress(event))),
    291      ("change alert", lambda event:
    292          event.set_alert("was: %s" % event.get_alert())),
    293  ]
    294
    295
    296  SUBEVENT_MODIFIERS = EVENT_MODIFIERS
    297
    298
    299  CONTAINER_MODIFIERS = [
    300      ("change time period", lambda event:
    301          event.set_time_period(event.get_time_period().move_delta(GregorianDelta.from_days(1)))),
    302      ("change text", lambda event:
    303          event.set_text("was: %s" % event.get_text())),
    304      ("change category", lambda event:
    305          event.set_category(new_cat(event))),
    306  ]
    307
    308
    309  CATEGORY_MODIFIERS = [
    310      ("change name", lambda category:
    311          category.set_name("was: %s" % category.get_name())),
    312      ("change id", lambda category:
    313          category.set_id(inc(category.get_id()))),
    314      ("change color", lambda category:
    315          category.set_color(category.get_color() + (1, 0, 3))),
    316      ("change font color", lambda category:
    317          category.set_font_color(category.get_font_color() + (1, 0, 3))),
    318      ("change parent", lambda category:
    319          category.set_parent(new_parent(category))),
    320  ]
    321
    322
    323  TIME_PERIOD_MODIFIERS = [
    324      ("zoom", lambda time_period: time_period.zoom(-1)),
    325      ("move left", lambda time_period: time_period.move(-1)),
    326      ("move right", lambda time_period: time_period.move(1)),
    327  ]
    328
    329
    330  ERA_MODIFIERS = [
    331      ("change id", lambda era: era.set_id(inc(era.get_id()))),
    332      ("change time period", lambda era: era.set_time_period(era.get_time_period().move_delta(GregorianDelta.from_days(1)))),
    333      ("change text", lambda era: era.set_name("was: %s" % era.get_name())),
    334      ("change color", lambda era: era.set_color(tuple([x + 1 for x in era.get_color()])))
    335  ]
    336
    337  NUM_ERA_MODIFIERS = [
    338      ("change id", lambda era: era.set_id(inc(era.get_id()))),
    339      ("change time period", lambda era: era.set_time_period(era.get_time_period().move_delta(1))),
    340      ("change text", lambda era: era.set_name("was: %s" % era.get_name())),
    341      ("change color", lambda era: era.set_color(tuple([x + 1 for x in era.get_color()])))
    342  ]
    343
    344
    345  TIME_MODIFIERS = [
    346      ("add", lambda time: time + GregorianDelta(1)),
    347  ]
    348
    349
    350 -class ObjectWithTruthValue(object):
    351      """An object of this class can be treated as a boolean."""
    352 -    def __init__(self, truth_value):
    353          self.truth_value = truth_value
    354
    355 -    def __bool__(self):
    356          return self.truth_value
    357
    358
    359 -def select_language(language):
    360      """
    361      Select the system locale language.
    362      This function is Windows specific.
    363      """
    364      import platform
    365      from timelinelib.config.paths import LOCALE_DIR
    366      from timelinelib.meta.about import APPLICATION_NAME
    367      if platform.system() == "Windows":
    368          import gettext
    369          import os
    370          os.environ['LANG'] = language
    371          gettext.install(APPLICATION_NAME.lower(), LOCALE_DIR)
    372
    373
    374 -class _ANY(object):
    375      """An object of this class is always considered equal to any other object."""
    376
    377 -    def __eq__(self, other):
    378          return True
    379  ANY = _ANY()
    380  """This object is always considered equal to any other object."""
    381
    382
    383 -def svg_to_dict(xml):
    384      def node_to_dict(node):
    385          return {
    386              "name": node.tag,
    387              "attributes": {key: attribute(key, value) for key, value in node.attrib.items()},
    388              "children": [node_to_dict(child) for child in node]
    389          }
    390      def attribute(key, value):
    391          if key == "style":
    392              styles = {}
    393              for style in value.strip().split(";"):
    394                  if style.strip():
    395                      name, value = style.split(":")
    396                      styles[name.strip()] = value.strip()
    397              return styles
    398          else:
    399              return value
    400      return node_to_dict(ET.fromstring(xml))
    401