Mandatory task

They decided to ignore the lockdown and continue to meet for coffee, fitness, ...

To describe who was where, we use lists like this.

obiski = [("Ana", "kava"), ("Berta", "kava"), ("Cilka", "telovadba"),
          ("Dani", "zdravnik"), ("Ana", "zdravnik"), ("Cilka", "kava"),
          ("Ema", "telovadba"), ("Fanči", "telovadba"),
          ("Greta", "telovadba")]

Write the following functions:

  • osebe(visits) gets a list like above, and returns a set of names of all persons appearing in it; for the above data it returns {"Ana", "Berta", "Cilka", "Dani", "Ema", "Fanči", "Greta"}.

  • aktivnosti(obiski) gets a list of the same form and returns a set of activities; in the above case, {"kava", "telovadba", "zdravnik"}.

  • udelezenci(aktivnost, obiski) gets a name of activity, like "kava", and a list like above. It returns a set of people who participated in the activity (like {"Ana", "Berta", "Cilka"}.)

  • po_aktivnostih(obiski) also gets a list like above, but returns a dictionary, whose keys are activities, and the corresponding values are sets of people participating at each activity, like

    {"kava": {"Ana", "Berta", "Cilka"},
     "zdravnik": {"Ana", "Dani"},
     "telovadba": {"Cilka", "Ema"}}
  • skupine(obiski) returns a list of sets of people who met at some activity - one set per activity. In the above case it returns [{"Ana", "Berta", "Cilka"}, {"Ana", "Dani"}, {"Cilka", "Ema"}]. The order of sets in the list can be arbitrary.

  • okuzeni(skupine, nosilci) gets a list of groups, like the one returned by the previous function, and a set of people for whom we know they are infected. The function returns a set of people who were potentially directly infected by the latter. For groups from the previous example, the call okuzeni(skupine, {"Cilka", "Berta"}) returns {"Ana", "Ema"}.

  • zlati_prinasalec(skupine) returns the name of the person that can infect the most people, that is, the name of the person who met with most other people. Note that this is not necessarily the person who appers in most groups. If there is an equal number of super spreaders, our "Zlatko" is the one that is the first alphabetically. In the above example, Ana and Cilka can infect three people, and the function returns Ana.

  • korakov_do_vseh(skupine, prvi), gets a list of groups and the name of the first infected person. It returns the number of steps after which everybody is sick.

    Say that our groups are

    [{"Cilka", "Ema", "Jana", "Saša"},
    {"Fanči", "Greta", "Saša"},
    {"Greta", "Nina"},
    {"Greta", "Olga", "Rebeka"},
    {"Micka", "Ana", "Klara"},
    {"Fanči", "Iva", "Berta", "Špela"},
    {"Klara", "Cilka", "Dani"},
    {"Petra", "Dani", "Lara", "Špela"}]

    and that the first infected person is Ana. Ana infects Micka and Klara. In the next step, they infect Dani and Cilka. In the third, these two infect Lara, Jana, Petra, Saša, Ema and Špela. In the fourts, they infect Berta, Fanči, Greta and Iva. On the fifth, they infect Olga, Nina and Rebeka, which wraps it up. Therefore, if we begin with Ana, spreading the disease to everybody takes five steps. The function returns 5.

    korak  okuženi
    0 Ana
    1 Micka, Klara
    2 Dani, Cilka
    3 Lara, Jana, Petra, Saša, Ema, Špela
    4 Berta, Fanči, Greta, Iva
    5  Olga, Nina, Rebeka

    If there exists an isolated person or a group to whom the disease cannot spread, the function must return None.

Extra task

Implement all functions except the last three (okuženi, zlati_prinasalec, korakov_do_vseh) using list/set/dictionary comprehension, so they will have only one line - a single return. You are of course not allowed to add extra functions that would actually do the job, and your one-line functions would then simply call them. (Tests will try to see to it that you don't pull this trick, but please don't be too innovative at circumventing them. :)

You are allowed to use any built-in functions and functions from any modules that come with Python.

An extra challenge

Implement okuženi and zlati_prinasalec in one line. Function functools.reduce can be useful (but its use is not mandatory), as well as some extra arguments for min, max or sort.