RLI

RLI TutorialPermalink

This tutorial uses package iplantuml for plotting UMLs

Installation:

  • install java: sudo apt-get install default-jre graphviz
  • download plantuml.jar from http://plantuml.com/ and put it into /usr/local/bin
  • install package: pip install iplantuml
  • import iplantuml
  • done

Visit http://plantuml.com/ as UML-code reference
Additional literature:
https://sourcemaking.com/design_patterns/mediator
https://de.wikipedia.org/wiki/Business_Process_Model_and_Notation
http://www.vertabelo.com/blog/technical-articles/chen-erd-notation

In [1]:
import iplantuml

UML WorkshopPermalink

The following UML diagrams will be explained in the following:

  1. Database diagrams (ERM)
  2. Class diagrams
  3. Activity diagrams

Database UMLs (Entity-Relationship-Model [ERM])Permalink

A very nice and easy to understand schema is called chen-notation. Nice overview can be found here: http://www.vertabelo.com/blog/technical-articles/chen-erd-notation The most important components will be discussed in the following.

Common components are:

EntitiesPermalink

In [2]:
%%plantuml --jar

@startuml
'ERM definitions:
skinparam tabSize 2
sprite $key [17x12/16z] bOqv3e1030CJRzPn9Fx_NWY7n4eqJ3TJs6OVa5pTpD-5tl3YyFHG-4DsqAOnWgawWp0r0KGagDuGMYMJxbMrBxzLPJ_O0G00
!define Table(name,desc) class name as "desc" << (T,#FFAAAA) >>
!define primary_key(x) <$key><b>x</b>
!define foreign_key(x) <$key>x
!define unique <b>U</b>
!define not_null <b>N</b> 
hide methods
hide stereotypes
                                                  
Table(t, "Tablename\n(Description)") {
    Column \t TYPE
    Column \t TYPE
}
note top of t: Simple definition
                                                  
@enduml
Out[2]:
Tablename(Description)ColumnTYPEColumnTYPESimple definition
In [3]:
%%plantuml --jar

@startuml
'ERM definitions:
skinparam tabSize 2
sprite $key [17x12/16z] bOqv3e1030CJRzPn9Fx_NWY7n4eqJ3TJs6OVa5pTpD-5tl3YyFHG-4DsqAOnWgawWp0r0KGagDuGMYMJxbMrBxzLPJ_O0G00
!define Table(name,desc) class name as "desc" << (T,#FFAAAA) >>
!define primary_key(x) <$key><b>x</b>
!define foreign_key(x) <$key>x
!define unique <b>U</b>
!define not_null <b>N</b> 
hide methods
hide stereotypes
                                                  
Table(t1, "Tablename\n(Description)") {
    <color:red>primary_key(id) \t INTEGER</color>
    --
    name \t VARCHAR
    value \t INTEGER
}
note top of t1: With primary key
                                                  
Table(t2, "Tablename\n(Description)") {
    primary_key(id) \t INTEGER
    --
    name \t VARCHAR<color:red>(255)</color>
    value \t INTEGER
}
note top of t2: Additional type information

Table(t3, "Tablename\n(Description)") {
    primary_key(id) \t INTEGER
    --
    name \t VARCHAR(255) \t <color:red>not_null</color>
    value \t INTEGER
}
note top of t3: Not-Null constraint
                                                  
Table(t4, "Tablename\n(Description)") {
    primary_key(id) \t INTEGER
    --
    name \t VARCHAR(255) \t not_null<color:red>unique</color>
    value \t INTEGER
}
note top of t4: Unique constraint
@enduml
Out[3]:
Tablename(Description)idINTEGERnameVARCHARvalueINTEGERWith primary keyTablename(Description)idINTEGERnameVARCHAR(255)valueINTEGERAdditional type informationTablename(Description)idINTEGERnameVARCHAR(255)NvalueINTEGERNot-Null constraintTablename(Description)idINTEGERnameVARCHAR(255)NUvalueINTEGERUnique constraint

RelationshipsPermalink

In [4]:
%%plantuml --jar

@startuml
'ERM definitions:
skinparam tabSize 2
sprite $key [17x12/16z] bOqv3e1030CJRzPn9Fx_NWY7n4eqJ3TJs6OVa5pTpD-5tl3YyFHG-4DsqAOnWgawWp0r0KGagDuGMYMJxbMrBxzLPJ_O0G00
!define Table(name,desc) class name as "desc" << (T,#FFAAAA) >>
!define primary_key(x) <$key><b>x</b>
!define foreign_key(x) <$key>x
!define unique <b>U</b>
!define not_null <b>N</b> 
!define foreign <b>FK</b> 
hide methods
hide stereotypes
       
'One-to-one'
Table(user, "user") {
primary_key(user_id) INTEGER
--
username \t VARCHAR
password \t VARCHAR
}
note top of user : One-to-One relationship

Table(user_profile, "user_profile\n(user has one pofile)") {
primary_key(user_id) \t INTEGER
--
age \t\t SMALLINT
email \t VARCHAR
}

user "1" -- "1" user_profile   

'One-to-many'                                                  
Table(user2, "user") {
primary_key(user_id) INTEGER
--
username \t VARCHAR
password \t VARCHAR
}
note top of user2 : One-to-Many relationship

Table(session, "session\n(user may have multiple sessions)") {
primary_key(session_id) \t INTEGER
--
foreign_key(user_id) \t INTEGER \t not_null foreign
start \t\t\t\t\t DATETIME
}

user2 "1" -- "N" session
                                                
'Many-to-many'
Table(user3, "user") {
primary_key(user_id) INTEGER
--
username \t VARCHAR
password \t VARCHAR
}
note top of user3 : Many-to-Many relationship
                                                  
Table(group, "group") {
primary_key(group_id) INTEGER
--
name \t VARCHAR \t not_null
}

Table(user_group, "user_group") {
primary_key(user_id) \t\t INTEGER \t foreign
primary_key(group_id) \t INTEGER \t foreign
--
joined_at DATETIME
}

user3 "1" -- "N" user_group
group "1" -- "N" user_group 
@enduml
Out[4]:
useruser_idINTEGERusernameVARCHARpasswordVARCHAROne-to-One relationshipuser_profile(user has one pofile)user_idINTEGERageSMALLINTemailVARCHARuseruser_idINTEGERusernameVARCHARpasswordVARCHAROne-to-Many relationshipsession(user may have multiple sessions)session_idINTEGERuser_idINTEGERNFKstartDATETIMEuseruser_idINTEGERusernameVARCHARpasswordVARCHARMany-to-Many relationshipgroupgroup_idINTEGERnameVARCHARNuser_groupuser_idINTEGERFKgroup_idINTEGERFKjoined_at DATETIME111N1N1N

Code examplePermalink

In [5]:
%%plantuml --jar

@startuml
'ERM definitions:
skinparam tabSize 2
sprite $key [17x12/16z] bOqv3e1030CJRzPn9Fx_NWY7n4eqJ3TJs6OVa5pTpD-5tl3YyFHG-4DsqAOnWgawWp0r0KGagDuGMYMJxbMrBxzLPJ_O0G00
!define Table(name,desc) class name as "desc" << (T,#FFAAAA) >>
!define primary_key(x) <$key><b>x</b>
!define foreign_key(x) <$key>x
!define unique <b>U</b>
!define not_null <b>N</b> 
!define foreign <b>FK</b> 
hide methods
hide stereotypes
                                                  
Table(run, "Run") {
    primary_key(run_id) INTEGER not_null
    --
    timestamp DATETIME not_null
}
Table(meta, "Meta") {
    primary_key(meta_id) INTEGER not_null
    --
    location \t\t\t\t VARCHAR \t not_null
    json \t\t\t\t\t\t\t JSON
    foreign_key(owner_id) \t\t INTEGER \t foreign not_null
    foreign_key(parent_id) \t INTEGER \t foreign
}
Table(source, "Source") {
    primary_key(source_id) INTEGER not_null
    --
    foreign_key(run_id) \t\t INTEGER \t foreign not_null
    foreign_key(root_id) \t INTEGER \t foreign not_null \t
    type \t\t\t\t\t VARCHAR \t not_null
    name \t\t\t\t VARCHAR \t not_null
    info \t\t\t\t\t VARCHAR
} 
Table(owner, "Owner") {
    primary_key(owner_id) INTEGER not_null
    --
    foreign_key(run_id) \t INTEGER \t not_null
    name \t\t\t VARCHAR \t not_null
}

run "1" -- "N" source
run "1" -- "N" owner
source "0..1" -- "1" meta
meta "1" -- "0..1" meta : parent
meta "N" -- "1" owner
@enduml
Out[5]:
Runrun_idINTEGERNtimestamp DATETIMENMetameta_idINTEGERNlocationVARCHARNjsonJSONowner_idINTEGERFKNparent_idINTEGERFKSourcesource_idINTEGERNrun_idINTEGERFKNroot_idINTEGERFKNtypeVARCHARNnameVARCHARNinfoVARCHAROwnerowner_idINTEGERNrun_idINTEGERNnameVARCHARN1N1N0..11parent10..1N1
In [6]:
import datetime as dt
import sqlalchemy as sqla
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

SCHEMA = 'public'


class Run(Base):
    __tablename__ = 'run'
    __table_args__ = {'schema': SCHEMA}

    run_id = sqla.Column(
        sqla.Integer,
        primary_key=True
    )
    timestamp = sqla.Column(sqla.DateTime, default=dt.datetime.utcnow)
    sources = relationship("Source", back_populates='run')
    owners = relationship("Owner", back_populates='run')


class Meta(Base):
    __tablename__ = 'meta'
    __table_args__ = {'schema': SCHEMA}

    meta_id = sqla.Column(
        sqla.Integer,
        primary_key=True
    )
    location = sqla.Column(
        sqla.VARCHAR,
    )
    json = sqla.Column(
        sqla.JSON,
        nullable=True
    )
    owner_id = sqla.Column(
        sqla.ForeignKey(f'{SCHEMA}.owner.owner_id')
    )
    owner = relationship("Owner", back_populates="metas")
    parent_id = sqla.Column(
        sqla.Integer, sqla.ForeignKey(f'{SCHEMA}.meta.meta_id'), nullable=True)
    children = relationship('Meta')


class Source(Base):
    __tablename__ = 'source'
    __table_args__ = {'schema': SCHEMA}

    source_id = sqla.Column(
        sqla.Integer,
        primary_key=True
    )
    run_id = sqla.Column(
        sqla.ForeignKey(f'{SCHEMA}.run.run_id')
    )
    run = relationship("Run", back_populates="sources")
    root_id = sqla.Column(sqla.ForeignKey(f'{SCHEMA}.meta.meta_id'))
    root = relationship("Meta", uselist=False)
    type = sqla.Column(sqla.VARCHAR)
    name = sqla.Column(sqla.VARCHAR)
    info = sqla.Column(sqla.VARCHAR, nullable=True)


class Owner(Base):
    __tablename__ = 'owner'
    __table_args__ = {'schema': SCHEMA}

    owner_id = sqla.Column(
        sqla.Integer,
        primary_key=True
    )
    run_id = sqla.Column(sqla.ForeignKey(f'{SCHEMA}.run.run_id'))
    run = relationship("Run", uselist=False, back_populates='owners')
    name = sqla.Column(
        sqla.VARCHAR
    )
    metas = relationship("Meta", back_populates='owner')

Class diagramsPermalink

Class definitionPermalink

In [7]:
%%plantuml --jar

@startuml
skinparam classAttributeIconSize 0
skinparam tabSize 2
class "class_name" as c1 {
    attribute1
    attribute2
    method1()
    method2()
}
note top of c1 : Simple class

class "class_name" as c2 {
    type1 \t attribute1
    type2 \t attribute2
    return_type1 \t method1(ptype1 param1)
    return_type2 \t method2(ptype1 param1, ptype2 param2)
}
note top of c2 : Class showing (return-)types

class "class_name" as c3 {
    + \t type1 \t public_attribute
    - \t\t type2 \t private_attribute
    # \t type2 \t protected_attribute
    + \t return_type1 \t public_method()
    - \t\t return_type2 \t private_method()
    # \t return_type1 \t protected_method()
}
note top of c3 : Class showing access types
@enduml
Out[7]:
class_nameattribute1attribute2method1()method2()Simple classclass_nametype1attribute1type2attribute2return_type1method1(ptype1 param1)return_type2method2(ptype1 param1, ptype2 param2)Class showing (return-)typesclass_name+type1public_attribute-type2private_attribute#type2protected_attribute+return_type1public_method()-return_type2private_method()#return_type1protected_method()Class showing access types

Relations between classesPermalink

Typical relations between classes

  • Extension: Shows inheritance between classes
  • Assoziation: Objects are somehow related (loose)
  • Aggregation: Object can contain other object
  • Composition: The child object can not exist without the parent object
In [8]:
%%plantuml --jar

@startuml
parent <|-- child : Inheritance
car "1" *-- "4" wheel : Composition
course "0..N" o-- "N" student : Aggregation
system -- process : Assoziation
@enduml
Out[8]:
parentchildcarwheelcoursestudentsystemprocessInheritanceComposition14Aggregation0..NNAssoziation
In [9]:
%%plantuml --jar

@startuml
class "Context" as context {
    # strategy strategy
    + void context_interface()
}
class "Strategy" as strategy {
    + void algorithm_interface() [abstract]
}
class "Strategy A" as strategy1 {
    + void algorithm_interface()
}
class "Strategy B" as strategy2 {
    + void algorithm_interface()
}
context o-- strategy
strategy <|-- strategy1
strategy <|-- strategy2
@enduml
Out[9]:
Contextstrategy strategyvoid context_interface()Strategyvoid algorithm_interface() [abstract]Strategy Avoid algorithm_interface()Strategy Bvoid algorithm_interface()
In [10]:
class Context:
    """
    Define the interface of interest to clients.
    Maintain a reference to a Strategy object.
    """

    def __init__(self, strategy):
        self._strategy = strategy

    def context_interface(self):
        self._strategy.algorithm_interface()


class Strategy(metaclass=abc.ABCMeta):
    """
    Declare an interface common to all supported algorithms. Context
    uses this interface to call the algorithm defined by a
    ConcreteStrategy.
    """

    @abc.abstractmethod
    def algorithm_interface(self):
        pass


class ConcreteStrategyA(Strategy):
    """
    Implement the algorithm using the Strategy interface.
    """

    def algorithm_interface(self):
        pass


class ConcreteStrategyB(Strategy):
    """
    Implement the algorithm using the Strategy interface.
    """

    def algorithm_interface(self):
        pass


def main():
    concrete_strategy_a = ConcreteStrategyA()
    context = Context(concrete_strategy_a)
    context.context_interface()


if __name__ == "__main__":
    main()
    
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-10-4afe926bdfc4> in <module>()
     12 
     13 
---> 14 class Strategy(metaclass=abc.ABCMeta):
     15     """
     16     Declare an interface common to all supported algorithms. Context

NameError: name 'abc' is not defined

Activity diagramPermalink

In [ ]:
%%plantuml --jar

@startuml
partition simple_method {
start
note left: Start
:action1;
:action2;
stop
note left: End
}

partition conditional_method {
start
if (check?) then (yes)
  :action1;
else (no)
  :action2;
endif
stop
}

partition forks_and_merges {
    start
    fork
    :fork1;
    fork again
    :fork2.1;
    :fork2.2;
    fork again
    :fork3;
    end
    note right: Terminates this branch
    end fork
    note left: Merge
    stop
}

partition simple_object {
    start
    : Set value to 1;
    :value=1]
    note left: Show current object
    stop
}

:input]
partition function {
    :Set value to five times input;
    :value=input*5]
    :Return value;
}
:value]
@enduml

Code examplePermalink

In [ ]:
%%plantuml --jar

@startuml

partition login {
start
:logged_in=False]
:User enters name and password;
if (Password correct?) then (yes)
:login;
:logged_in=True]
else (no)
:Show "Invalid password";
endif
}
:logged_in]

@enduml
In [ ]:
def login():
    logged_in = False
    name = input("Name")
    password = input("Password")  # Please do this safely in real functions!
    if password == "123456":
        logged_in = True
    else:
        print("Invalid password")
    return logged_in

login()