0% found this document useful (0 votes)
14 views21 pages

Project Airspace Routes 2024 - Phase 1

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views21 pages

Project Airspace Routes 2024 - Phase 1

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 21

INFO1

DESCRIPTION OF THE PROJECT PROGRAMMING TASKS

Ruben Hidalgo
Cristina Barrado
Miguel Valero
Albert Ruzafa

Computer Architecture Department


EETAC - UPC
April 2024
INFO1 Project

Table of contents

INTRODUCTION OF THE PROJECT.............................................................................................. 3


Project development....................................................................................................................4
Quality criteria............................................................................................................................... 7
Phase ZERO: Guided Mini-Project............................................................................................... 10
Waypoint class and initial functions............................................................................................ 11
FlightPlan class and initial functions...........................................................................................11
Advanced functions.................................................................................................................... 13
Main program for Phase ZERO..................................................................................................14
Deliver of Phase ZERO.............................................................................................................. 14
PHASE 1 - DEFINITION OF GRAPH.............................................................................................. 15
Node class and initial functions.................................................................................................. 17
Segment class and initial functions............................................................................................ 18
Graph class and initial functions.................................................................................................18
Main program for Phase 1..........................................................................................................19
Deliver of Phase 1...................................................................................................................... 21

Page 2
INFO1 Project

INTRODUCTION OF THE PROJECT

Imagine that we want to go from Sabadell to Igualada and want to calculate the best
route on the map:

In a straight line the distance is 40.77 Km. The problem is that we cannot go in a straight
line because we have to use the roads that connect the points on the map, so in fact we
will be traveling 61.3 Km.

The purpose of this project is to build a data model (classes / structs) that represents a
route network like the one above but for the air, which will allow you to nd the
minimum distance path to go from one place to another.

The project has a warm-up mini-project and follows the actual project divided into 4
phases (1 per week) to incrementally build your nal program. The listing below
summarizes the tasks to be done in each phase:

First phase: the graph


Build the data model of a node, a segment and a graph to store the points
(nodes) and their connections (segments).
Build several basic functions to plot and visualize the graph.
Write a test program to load the graph with a very simple set of points and
connections and verify the functions.

Second phase: the route


Build the data model that represents a path (or route) in the graph.
Write a test program to dene a path (or route) in a graph that contains a set of
connected points.
Write a program to compute the minimum path (the shortest distance) between
two nodes in the graph.
Build functions to visualize a path in the graph to verify that we have built it
correctly.

Page 3
INFO1 Project

Write a test program to verify that the minimum path from an origin to a
destination is computed correctly.

Third phase: the airspace


Use the previous structure and program to adapt it to the European airspace.
Load the nodes that represent the airports and the air navigation points.
Load the segments that represent the airways (the “roads” in the sky …).
Verify that the program is capable of calculating the route with minimum
distance between two airports .

Fourth phase: nal


Build the functions that export les in kml format, which can then be loaded
using Google Earth and visualized on a real map.
Build the nal program , which will have a menu that will allow the end-users to
choose the airports and navigation les that they want, obtain the minimum path
between two given airports, and generate the kml les that allow visualizing the
result using Google Earth.

Project development

● MEETINGS
The project lasts the nal 5 classes of the course, and thus, the work has to be done
during the 4 weeks in between the 5 classes. A personal dedication of at least 10 hours
per week is expected, including the class hours. Students will work in groups of 4 people,
as published in ATENEA. Groups have to meet twice per week: one meeting during class
and at least a second one out of the class hours. For this reason the students must agree
(and sign) the established time for the meetings outside of the class schedule during the
rst project class. The reserved time for this second meeting must be 2 hours, and can
be face-to-face or using any communications tool. The reserved time and the
location/tool must appear in the signed agreement. The rest of the time, until reaching
the 10 hours limit, must be personal work.
At the end of each meeting one person of the group has to write the summary of the
meeting. This is a document that must contain:
1. group name, date, time, duration and list of assistants,
2. review of code contributed by each member,
3. results of the integration.
4. division of the pending tasks til the next meeting
All meeting minutes will be delivered immediately in ATENEA progress reports section
after ending the meeting.

● DELIVERABLES
In addition to the progress reports, the project has 11 deliverables, more than one per
week. Some are individual (D##) and some are group (G#) deliverables. Look at the
gure below (taken from ATENEA) to have a complete view of the deliverables. At the
group kick-o meeting G1 must be delivered at the end of the class. The rest are
homework deliverables. Individual deliverables count for the Personal Continuous Mark.

Page 4
INFO1 Project

Be sure you deliver on time to reach the minimum 80% of them! Group deliverables are
G1 (group contitution), G0 (delivery of Phase ZERO), G2 (code deliverable for Phase1+2)
and G3 (Phase3+4 -nal- code). Only G3 will be used for the project grade.

The calendar is as follows:

Page 5
INFO1 Project

23-04 - Kick-o: Phase ZERO


Deliver Group G1
28-04 - Publication of Ph. ZERO solution
29-04 - Deliver Group G0 + Individual D16
30-04 - Phase 1 starts

06-05 - Deliver Individual D17


07-05 - Phase2 starts
13-05 - Deliver Group G2 + Individual D18
14-05 - Phase3 starts
20-05 - Deliver Individual D19
21-05 - Phase4 starts
27-05 - Deliver Group G3
+ Individuals D20, D21, D22
28-05 - Cross-evaluation
+ Extension exam

● EXAMS
There will be 2 exams, both individual, both using the computer. The rst exam will be
done during class without previous notication. Duration is 30-40 minutes. The second
exam will be done during the last class. Based on the delivered nal version of the
project, each student will be requested to expand the project with a new functionality.
The time for this last exam will be 1h30m . The new functionality must be working at the
end of the exam.

● CROSS-EVALUATION
The last class, and just before the nal project exam, each group will review the work
done by the other two groups. For this, two laptops of each group will hold in the
desktop a prepared copy of the project code, so the reviewing groups will easily nd it.
They will follow an execution guide and check the correctness of the delivered project.
This activity is a mandatory learning activity that has the objective that you learn to
understand code from others, learn from it and see new ways for solving similar
problems. In addition, at the end of the project, each member will have to evaluate their
project peers.

● TRANSVERSAL COMPETENCES

Page 6
INFO1 Project

In order to fulll the legal mandate, this course must work some transversal
competences. To complete them you have to be sure to deliver D20 and D21 which hold
the transversal competences on communication, critical thinking, social compromise and
sustainability.

● GRADES
The project is a 40% of the course grade, and it is divided as follows:
• 25% for the project code (the last deliverable) and its management reports. This is a
common grade for all the components of the group, except if the minutes of the
meetings show that not all have worked suciently.
• 5% for the rst exam done in writing individually
• 10% for the second exam done on the computer individually the last day.

Quality criteria

Python is a language that has no explicitly declared types. This implies that seeing the
header of a function we can not know the types of data expected by this function. To
solve this lack of detail you must follow the rules of the next paragraph:

A program must be correctly commented:

Always write a comment in the header of each class, indicating its initialization
and the description of the elds that the class has.
Write a comment in the header of each function, indicating the type of each
argument and the type of the return value. Describe the role of each of the
arguments and of the return value.

For example:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import math

""" Vector (x: float, y: float)


===========================
Defines class Vector (build as a vector in 2D)
x: float, component x of the vector.
y: float, component y of the vector.
"""
class Vector:
def __init__ (self, x, y):
self.x = x
self.y = y

""" norma (v: Vector): float


=========================
Calculates the norm of a vector
v: Vector, vector to which function shall calculate the norm
return: float, norm of the vector v
"""

Page 7
INFO1 Project

def norma (v):


return math.sqrt(v.x**2 + v.y**2)

""" MAIN PROGRAM


Test the correct functioning of class vector and its functions.
"""

v1 = Vector (3,4)
n = norma (v1)
print ("La norma vale:",n)

This does not mean that your comments should be EXACTLY the same as the previous
one, there are many formats. But your code must allow you to understand through the
comments the purpose of all declared functions and classes.

Project Assessment Criteria


Quality Level
Quality Item Good (all Fair (half of the Low (0 points)
points) points)
Functionality The program One of the Two or more
(4 points) implements mandatory functions are not
correctly ALL the functions is not implemented, OR
mandatory implemented, OR the program fails
functions. two or more tests more frequently
A wide range of have failed. than it succeeds.
tests have It is not working
worked ne or as supposed.
only one minor
failure has been
found.
Robustness The program is The program is The program is
(1.5 points) able to deal with fairly robust. Only not robust at all. It
unexpected strange crashes
situations and unexpected frequently and
non-conforming situations make fails to properly
data les. It never the program to deal with typical
crashes or crash errors.
suddenly stops
due to syntax or
execution errors.
Friendliness The user has no In general the The user
(1.5 points) doubts regarding information and constantly doubts
how to interact messages with regard to
with the provided by the what are the
application. The application are inputs the
expected inputs clear enough. program expects
are clear and the Nevertheless, or how to
results and there are

Page 8
INFO1 Project

output messages occasions when it interpret its


are easy to is not easy to results.
understand. know what to do
or to interpret its
outputs.
Code The code of the The code is Code organization
organization (3 program is reasonably well is messy and there
points) organized in a organized and is a clear lack of
clean way, with commented. In comments (or if
good indentation, some cases, present, they
has useful though, these don't clarify
comments and aspects could be anything). Code is
variable names improved. not well indented.
have a clear Additional It would be very
meaning. In case comments would hard to modify
of needing a code clarify certain this code in order
modication, it is parts of the x a bug or add
easy to program. some piece of
implement. functionality.

Page 9
INFO1 Project

Phase ZERO: Guided Mini-Project


Write the basic data structure containing information about the flight plan of a drone, consisting of a
list of waypoints, and some basic functionalities to process the information.
The data structure (class diagram) that you will need is the following:

Therefore, you need to build a flight plan class and an waypoint class, with the following fields:

Waypoint
- latitude (a number)
- longitude (a number)
- waypoint name (a text)

FlightPlan
- route (a vector of Waypoint, that the drone will follow in order, only one-way)
- the total route distance (a number)

To program the previous classes go through the following steps:

1. Create a waypoint.py file and write the class Waypoint and the basic functions given
below. Write also a test program test_waypoint.py to test the functions of the class.
2. Create a flightplan.py file and write the class FlightPlan and the functions given below.
Write also a test program test_flightplan.py to test all the functions of the class.
3. Add some more advanced functions in the waypoint.py file and extend the test program
test_waypoint.py to test the advanced functions.
4. Write a final test_phaseZERO.py program to test both classes with an interactive menu
with the end user.

Test programs shall import the python modules where the class and its defined functions are.

BUILD TEST PROGRAMS THAT VERIFY ALL THE INPUT CONDITIONS, INCLUDING
ERRONEOUS PARAMETERS, TO VERIFY THAT THEY WORK CORRECTLY.

Page 10
INFO1 Project

Waypoint class and initial functions


• Waypoint

Create a class Waypoint and the following functions in file waypoint.py:

Waypoint ()
Input parameters: None. Output parameter: an empty object of class Waypoint
Description: Constructor

LocateWaypoint (wp, lat, lon)


Input parameters: one waypoint and 2 numbers. Output parameter: None
Description: Sets attributes latitude and longitude

NameWaypoint (wp, name)


Input parameters: one waypoint and one string. Output parameter: None
Description: Sets attribute name

ShowWaypoint (wp)
Input parameters: one waypoint. Output parameter: None
Description: Shows on the screen the information of the Waypoint.

Additionally, write a test program for the class Waypoint test_waypoint.py that declares 3
Waypoints and adds them into a list. Finally, it shows the content of the list in the console to check
it works correctly.

FlightPlan class and initial functions


• FlightPlan

Create a class FlightPlan and the following functions in file flightplan.py:

FlightPlan ()
Input parameters: None. Output parameter: an empty object of class FlightPlan
Description: constructor.

SetDeparture (fp, wp)


Input parameters: one FlightPlan and one Waypoint. Output parameter: number
Description: Updates an empty FlightPlan with wp as its first route element. Returns a 0 if
the FlightPlan was empty or -N if it had some (N) elements.

AddWaypoint (fp, wp)


Input parameters: one FlightPlan and one Waypoint. Output parameter: number
Description: Updates a non-empty FlightPlan with a new Waypoint at the end of the current
route list. Returns a -1 if the FlightPlan was empty or 0 otherwise.

ShowFlightPlan (fp)

Page 11
INFO1 Project

Input parameters: one FlightPlan. Output parameter: None


Description: Shows on the screen the information of the list. It shall use the function
ShowWaypoint described above.

Additionally, write a test program for the class FlightPlan test_flightplan.py that creates a
FlightPlan and then shows the content of the Waypoint list in the console to check it works
correctly.

Page 12
INFO1 Project

Advanced functions
Write new additional functions for the two classes
Write in the waypoint.py file.

CalculateDistance (wp1, wp2)


Input parameters: two Waypoints. Output parameter: a number
Description: calculates the Haversine distance (great circle distance) of the two points.
NOTE: See Haversine formula in Wikipedia and remember to convert degrees to radians
before using trigonometric functions.

(ie. r = 6335.439 km in the Equator)

Write in the flightplan.py file.

FinishFlightPlan (fp)
Input parameters: a FlightPlan. Output parameter: number.
Description: Sets the attribute total route distance of flight plan. This function shall use the
function CalculateDistance described above. Returns a -1 if the FlightPlan was empty or the
calculated distance otherwise.

SearchWaypointByName (fp, name)


Input parameters: a FlightPlan. Output parameter: number
Description: Loops over the route of the flight plan searching for a Waypoint with the name
given as parameter. Returns the position of the waypoint in the vector or -1 if not found.

PlotFlightPlan (fp)
Input parameters: a FlightPlan. Output parameter: number.
Description: Shows a plot with the flight plan route. Assume that geographic coordinates can
be used in a cartesian plot. Returns a -1 if the FlightPlan was empty or the calculated
distance otherwise.

LoadFlightPlan (filename, fp)


Input parameters: a filename and an empty flight plan. Output parameter: a number.
Description: Read flight plan route. Assume that the file will contain an indeterminate
number of lines and in each line there will be two float numbers (latitude and longitude) and

Page 13
INFO1 Project

a name, all separated by a space character. Returns a -1 if the filename does not exist or the
number of lines/waypoints if it exists.

IsPath (fp, name1, name2)


Input parameters: a FlightPlan and 2 strings. Output parameter: boolean.
Description: checks if the flight plan route has a path to connect the waypoint with name1
with the waypoint with name2. This function shall use the waypoint function
SearchWaypointByName. Returns True if there is such a path or False if the FlightPlan is
empty or the route does not exist.

Main program for Phase ZERO


Write a new main program test_phaseZERO.py using the two test programs before and extend it to
check all the functionalities of Phase ZERO. Do it in a dynamic way in the form of a menu of
choices for the end-user.
The test program will loop on these two steps:
○ informing the end-user about the available functionalities, and
○ executing the selected option.
The available options must be, at least, the ones shown below. The loop finishes when the user
selects to end -option (0)-.

1. Create a Waypoint and add to a new FlightPlan


2. Create a Waypoint and add to the existing FlightPlan
3. Finish the FlightPlan and show the distance
4. Load FlightPlan from a file
5. Show the FlightPlan
6. Plot the FlightPlan
7. Check if a path exists in the FlighPlan
0. End program

Deliver of Phase ZERO


Work with your group in these functionalities. Once the solution is published go into the solution,
checking for the correctness of your code. Correct your solution if needed.
As deliverable the group shall write a document with:
● self-assessment of the group’s code using the quality criteria table given above. Give a grade
to the group work using the Quality criteria of the Introduction Section and explain it.
● take a screenshot of the program while correctly running on each computer of the group
members and add them to the report. Be ready to show to the teacher during the next class
that the Phase ZERO is running on all computers.
Deliverable of Phase ZERO must be submitted as G0 before the next class in ATENEA.

Page 14
INFO1 Project

PHASE 1 - DEFINITION OF GRAPH

We want to generate a data structure capable of storing the following information (for
example):

The nodes of the graph are points A, B, C, D, ... . Each of these nodes has a name, an
x-coordinate, and a y-coordinate.
Nodes may be interconnected through segments, which consist of a source node, a
destination node, and a cost to get from the origin to the destination. Since we are
building a data model to calculate minimum distances, the initial cost will be set to the
Euclidean distance between the two nodes. If this were a GPS device, the cost could be
either distance or time (estimating a specic trac and an average speed between each
segment), as we would like to minimize either the distance or the time traveled.

Page 15
INFO1 Project

The data structure (class diagram) that we will need is the following:

A graph is composed by a list of nodes and the list1 of segments that connect those
nodes.
Each node has a list of nodes that are its neighbors, this is, which are directly connected.
Each segment has a source node and a destination node.

Therefore, we need to build a Node class , a Segment class and a Graph class, with the
following elds:

Node
- Name (a text)
- coordinate x (a numerical oat)
- coordinate y (a numerical oat)
- A list of neighbors (the list of nodes directly connected the current node)

Segment
- Origin node (a Node)
- Destination node (a Node)
- Cost (a numeric oat, which indicates the distance between the source node and the
destination node)

Graph
- Nodes (list of graph nodes)
- Segments (list of segments of the graph)

To program the previous classes go through the following steps:

1. Create a node.py le and write the class Node and the basic functions given
below. Write also a test program test_node.py to test the functions of the class.
2. Create a segment.py le and write the class Segment and the functions given

1
A list is a vector with dynamic size. To create an empty list L use L=[]. To add an element to the list use
L.append(elem) and to remove it use L.remove(elem). Rest of usages are as in vectors.

Page 16
INFO1 Project

below. Write also a test program test_segment.py to test all the functions of the
class.
3. Create a graph.py le and write the class Graph and the functions given below.
Write also a test program test_graph.py to test all the functions of the class.

You can write additional functions if you think they may be convenient so that your code
is neat, understandable, and it allows you to avoid repeating the same instructions in
several places. But remember to add the comments explaining the functionality and the
parameters.

Test programs shall import the python modules where the class and its dened
functions are.

BUILD TEST PROGRAMS THAT VERIFY ALL THE INPUT CONDITIONS, INCLUDING
ERRONEOUS PARAMETERS, TO VERIFY THAT THEY WORK CORRECTLY.

Node class and initial functions


• Node

Create a class Node and the following functions in file node.py:

Node (name, x, y)
Input parameters: The name of the node and the horizontal and vertical cartesian location of
the node. Output parameter: an object of class Node with the given values and an empty list
of neighbors
Description: Constructor.

addNeighbor (n1, n2)


Input parameters: Two Nodes. Output parameter: boolean
Description: The first node n1 gets its list of neighbors extended with the second node n2.
Returns True if the operation is successful or False if the second node was already in the list.

distance (n1, n2)


Input parameters: Two Nodes. Output parameter: Number
Description: Return the Euclidean distance between the two nodes. For now, calculate the
Euclidean distance (the norm of the vector), but think that this should change when the
coordinates of the nodes represent degrees of latitude / longitude and not Cartesian
coordinates.

Additionally, write a test program for the class Node test_node.py using as a base the code below,
that declares 3 Nodes and adds the first two as neighbors of the third. Finally, it shows the content
of the third Node in the console to check it works correctly. Extend to test the error conditions.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

Page 17
INFO1 Project

import node

""" ===========================================================================================
Program to test the node
"""
#Main

print ("Probando nodo...")

n1 = node.Node("A",1,20)
n2 = node.Node("B",8,17)
n3 = node.Node("C",15,20)
addNeighbor (n3, n1)
addNeighbor (n3, n2)

print(n3.__dict__)
for n in n3.neighbors:
print (n.__dict__)

print ("OK")

Segment class and initial functions


• Segment

Create a class Segment and the following functions in file segment.py:

Segment (n1, n2)


Input parameters: Two nodes. Output parameter: a Segment
Description: Constructor: Calculates the distance using the function above and updates also
the list of neighbors of n1.

Additionally, write a test program for the class Segment test_segment.py that declares 3 Nodes and
creates a Segment between the first and the second and another between the second and the third.
Check that the segment distance and the list of neighbors of the nodes is correctly updated.

Graph class and initial functions


• Graph

Create a class Graph and the following functions in file graph.py:

Graph ()
Input parameters: None. Output parameter: a Graph
Description: constructor: Creates an empty Graph.

addNode (g, n)
Input parameters: a Graph and a Node. Output parameter: boolean
Description: Adds the node n in the list of nodes of graph g. Returns True if the operation is

Page 18
INFO1 Project

successful or False if the node was already in the list (the node name shall be the unique
identifier).

addSegment (g, name1, name2)


Input parameters: a Graph and two strings. Output parameter: boolean
Description: Creates a segment that connects the node of the graph with name1 with the
node of the graph with name2, then adds the segment in the list of segments of graph g.
Moreover, it updates the list of neighbors of the origin node with the destination node.
Returns True if the operation is successful or False if any of the names are not found in the
list of nodes of the graph.

plot (g)
Input parameters: a Graph. Output parameter: None
Description: Shows on the screen a plot of the graph, showing the nodes, the segments, and
the costs of the segments.

plotNode (g, name)


Input parameters: a Graph and a string. Output parameter: None
Description: Shows on the screen the subplot of the graph, with all its nodes and only the
segments to the neighbor nodes of the indicated node, together with their corresponding
costs.

Main program for Phase 1


Additionally, write a test program for the class Graph test_graph.py using as a base the program
given below. Look at the function that generates the graph (crearGrafoEjemplo), it adds the nodes
with their names and coordinates, then it adds the segments that connect the nodes. Extend to test
the error conditions.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import node
import segment
import graph

""" ===========================================================================================
Program to test the graph
"""

def crearGrafoEjemplo ():


G = graph.Graph()
graph.addNode(G, node.Node("A",1,20))
graph.addNode(G, node.Node("B",8,17))
graph.addNode(G, node.Node("C",15,20))
graph.addNode(G, node.Node("D",18,15))
graph.addNode(G, node.Node("E",2,4))
graph.addNode(G, node.Node("F",6,5))
graph.addNode(G, node.Node("G",12,12))
graph.addNode(G, node.Node("H",10,3))
graph.addNode(G, node.Node("I",19,1))
graph.addNode(G, node.Node("J",13,5))
graph.addNode(G, node.Node("K",3,15))
graph.addNode(G, node.Node("L",4,10))

Page 19
INFO1 Project

graph.addSegment(G, "A","B")
graph.addSegment(G, "A","E")
graph.addSegment(G, "A","K")
graph.addSegment(G, "B","A")
graph.addSegment(G, "B","C")
graph.addSegment(G, "B","F")
graph.addSegment(G, "B","K")
graph.addSegment(G, "B","G")
graph.addSegment(G, "C","D")
graph.addSegment(G, "C","G")
graph.addSegment(G, "D","G")
graph.addSegment(G, "D","H")
graph.addSegment(G, "D","I")
graph.addSegment(G, "E","F")
graph.addSegment(G, "F","L")
graph.addSegment(G, "G","B")
graph.addSegment(G, "G","F")
graph.addSegment(G, "G","H")
graph.addSegment(G, "I","D")
graph.addSegment(G, "I","J")
graph.addSegment(G, "J","I")
graph.addSegment(G, "K","A")
graph.addSegment(G, "K","L")
graph.addSegment(G, "L","K")
graph.addSegment(G, "L","F")
return G

#Main
print ("Probando el grafo...")

G = crearGrafoEjemplo()
graph.plot(G)
graph.plotNode(G, "C")

print ("OK")

The result of the execution of this program must be (more or less) the image below, which is made
using plots.

This graph shows the nodes as at points and the


name in bold, the segments as cyan arrows,
indicating the origin and destination, and the cost
of going from the rst to the second (the Euclidean
distance in the graph).

To make a fairly nice graph you will have to


investigate about the following pyplot functions:

- plt.plot, to paint the points.


- plt.text, to paint names and costs.
- plt.arrow, to paint the arrows of the segments.
- plt.axis, if you need to dene the total window
displayed
- plt.grid, to paint the division lines.
- plt.title, to give a title to the graphic.
- plt.show, to show the graph once it has been built.

Page 20
INFO1 Project

CONGRATULATIONS!!!
You already have a Graph class able to add a list of nodes, dene connections between
them, and show them in plots.

Deliver of Phase 1
Work with your group in these functionalities. For this phase deliver only the individual work done
before the next class in ATENEA, and the meeting summaries everytime the group finishes a
meeting.

Page 21

You might also like