#!/usr/bin/env python3
# Family people (c) Baltasar 2019 MIT License <baltasarq@gmail.com>
import csv
import json
import sys
from io import StringIO
class Person:
def __init__(self, name, surname, parent):
self._name = name
self._surname = surname
self._parent = parent
self._children = People()
@property
def name(self):
return self._name
@property
def surname(self):
return self._surname
@property
def parent(self):
return self._parent
@property
def children(self):
return self._children
def __str__(self):
toret = self.surname + ", " + self.name + " (" + self.parent + ")";
if self.children:
toret += " (" + str(self.children) + ")"
return toret
class JsonPeopleEncoder(json.JSONEncoder):
@staticmethod
def basic_encode_person(p):
return {"name": p.name,
"surname": p.surname,
"kids": len(p.children),
"children": []}
@staticmethod
def encode_people(people):
toret = []
for p in people:
p_dict = JsonPeopleEncoder.basic_encode_person(p)
if p.children:
p_dict["children"].append(
JsonPeopleEncoder.encode_people(p.children))
toret += [p_dict]
return toret
def default(self, obj):
if isinstance(obj, People):
return JsonPeopleEncoder.encode_people(obj)
# Let the base class default method raise the TypeError
return json.JSONEncoder.default(self, obj)
class People:
def __init__(self):
self._persons = []
def add(self, p):
self._persons.append(p)
def __add__(self, p):
self._persons.append(p)
return self
def __true__(self):
return bool(self._persons)
def __len__(self):
return len(self._persons)
def __getitem__(self, item):
return self._persons[item]
def __str__(self):
return str.join("\n", [str(p) for p in self._persons])
def _put_children_inside_persons(self):
dict_names = {p.name:p for p in self._persons}
people_to_remove = []
for p in self._persons:
if p.parent and p.parent != "null":
parent = dict_names.get(p.parent)
if parent:
parent.children.add(p)
people_to_remove += [p]
for p in people_to_remove:
return
def as_json(self):
return json.dumps(self, indent=4, cls=JsonPeopleEncoder)
@staticmethod
def read(fd):
toret = People()
reader = csv.DictReader(fd, delimiter=',')
for row in reader:
toret += Person(
row["name"],
row["surname"],
row["parent"])
toret._put_children_inside_persons()
return toret
if __name__ == "__main__":
with StringIO(sys.stdin.read()) as strf:
ps = People.read(strf)
print("People:")
print(ps)
print("\nJSON:")
print(ps.as_json())
IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoKIyBGYW1pbHkgcGVvcGxlIChjKSBCYWx0YXNhciAyMDE5IE1JVCBMaWNlbnNlIDxiYWx0YXNhcnFAZ21haWwuY29tPgoKCmltcG9ydCBjc3YKaW1wb3J0IGpzb24KaW1wb3J0IHN5cwpmcm9tIGlvIGltcG9ydCBTdHJpbmdJTwoKCmNsYXNzIFBlcnNvbjoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBuYW1lLCBzdXJuYW1lLCBwYXJlbnQpOgogICAgICAgIHNlbGYuX25hbWUgPSBuYW1lCiAgICAgICAgc2VsZi5fc3VybmFtZSA9IHN1cm5hbWUKICAgICAgICBzZWxmLl9wYXJlbnQgPSBwYXJlbnQKICAgICAgICBzZWxmLl9jaGlsZHJlbiA9IFBlb3BsZSgpCiAgICAgICAgCiAgICBAcHJvcGVydHkKICAgIGRlZiBuYW1lKHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLl9uYW1lCiAgICAKICAgIEBwcm9wZXJ0eQogICAgZGVmIHN1cm5hbWUoc2VsZik6CiAgICAgICAgcmV0dXJuIHNlbGYuX3N1cm5hbWUKICAgIAogICAgQHByb3BlcnR5CiAgICBkZWYgcGFyZW50KHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLl9wYXJlbnQKICAgIAogICAgQHByb3BlcnR5CiAgICBkZWYgY2hpbGRyZW4oc2VsZik6CiAgICAgICAgcmV0dXJuIHNlbGYuX2NoaWxkcmVuCiAgICAKICAgIGRlZiBfX3N0cl9fKHNlbGYpOgogICAgICAgIHRvcmV0ID0gc2VsZi5zdXJuYW1lICsgIiwgIiArIHNlbGYubmFtZSArICIgKCIgKyBzZWxmLnBhcmVudCArICIpIjsKICAgICAgICAKICAgICAgICBpZiBzZWxmLmNoaWxkcmVuOgogICAgICAgICAgICB0b3JldCArPSAiICgiICsgc3RyKHNlbGYuY2hpbGRyZW4pICsgIikiCiAgICAgICAgICAgIAogICAgICAgIHJldHVybiB0b3JldAoKCmNsYXNzIEpzb25QZW9wbGVFbmNvZGVyKGpzb24uSlNPTkVuY29kZXIpOgogICAgQHN0YXRpY21ldGhvZAogICAgZGVmIGJhc2ljX2VuY29kZV9wZXJzb24ocCk6CiAgICAgICAgcmV0dXJuIHsibmFtZSI6IHAubmFtZSwKICAgICAgICAgICAgICAgICJzdXJuYW1lIjogcC5zdXJuYW1lLAogICAgICAgICAgICAgICAgImtpZHMiOiBsZW4ocC5jaGlsZHJlbiksCiAgICAgICAgICAgICAgICAiY2hpbGRyZW4iOiBbXX0KICAgIAogICAgQHN0YXRpY21ldGhvZAogICAgZGVmIGVuY29kZV9wZW9wbGUocGVvcGxlKToKICAgICAgICB0b3JldCA9IFtdCiAgICAgICAgICAgIAogICAgICAgIGZvciBwIGluIHBlb3BsZToKICAgICAgICAgICAgcF9kaWN0ID0gSnNvblBlb3BsZUVuY29kZXIuYmFzaWNfZW5jb2RlX3BlcnNvbihwKQogICAgICAgICAgICAKICAgICAgICAgICAgaWYgcC5jaGlsZHJlbjoKICAgICAgICAgICAgICAgIHBfZGljdFsiY2hpbGRyZW4iXS5hcHBlbmQoCiAgICAgICAgICAgICAgICAgICAgSnNvblBlb3BsZUVuY29kZXIuZW5jb2RlX3Blb3BsZShwLmNoaWxkcmVuKSkKCiAgICAgICAgICAgIHRvcmV0ICs9IFtwX2RpY3RdCiAgICAgICAgICAgIAogICAgICAgIHJldHVybiB0b3JldAogICAgICAgIAogICAgZGVmIGRlZmF1bHQoc2VsZiwgb2JqKToKICAgICAgICBpZiBpc2luc3RhbmNlKG9iaiwgUGVvcGxlKToKICAgICAgICAgICAgcmV0dXJuIEpzb25QZW9wbGVFbmNvZGVyLmVuY29kZV9wZW9wbGUob2JqKQogICAgICAgIAogICAgICAgICMgTGV0IHRoZSBiYXNlIGNsYXNzIGRlZmF1bHQgbWV0aG9kIHJhaXNlIHRoZSBUeXBlRXJyb3IKICAgICAgICByZXR1cm4ganNvbi5KU09ORW5jb2Rlci5kZWZhdWx0KHNlbGYsIG9iaikKCgpjbGFzcyBQZW9wbGU6CiAgICBkZWYgX19pbml0X18oc2VsZik6CiAgICAgICAgc2VsZi5fcGVyc29ucyA9IFtdCiAgICAgICAgCiAgICBkZWYgYWRkKHNlbGYsIHApOgogICAgICAgIHNlbGYuX3BlcnNvbnMuYXBwZW5kKHApCiAgICAgICAgCiAgICBkZWYgX19hZGRfXyhzZWxmLCBwKToKICAgICAgICBzZWxmLl9wZXJzb25zLmFwcGVuZChwKQogICAgICAgIHJldHVybiBzZWxmCiAgICAgICAgCiAgICBkZWYgX190cnVlX18oc2VsZik6CiAgICAgICAgcmV0dXJuIGJvb2woc2VsZi5fcGVyc29ucykKICAgICAgICAKICAgIGRlZiBfX2xlbl9fKHNlbGYpOgogICAgICAgIHJldHVybiBsZW4oc2VsZi5fcGVyc29ucykKICAgIAogICAgZGVmIF9fZ2V0aXRlbV9fKHNlbGYsIGl0ZW0pOgogICAgICAgIHJldHVybiBzZWxmLl9wZXJzb25zW2l0ZW1dCiAgICAKICAgIGRlZiBfX3N0cl9fKHNlbGYpOgogICAgICAgIHJldHVybiBzdHIuam9pbigiXG4iLCBbc3RyKHApIGZvciBwIGluIHNlbGYuX3BlcnNvbnNdKQogICAgCiAgICBkZWYgX3B1dF9jaGlsZHJlbl9pbnNpZGVfcGVyc29ucyhzZWxmKToKICAgICAgICBkaWN0X25hbWVzID0ge3AubmFtZTpwIGZvciBwIGluIHNlbGYuX3BlcnNvbnN9CiAgICAgICAgcGVvcGxlX3RvX3JlbW92ZSA9IFtdCiAgICAgICAgCiAgICAgICAgZm9yIHAgaW4gc2VsZi5fcGVyc29uczoKICAgICAgICAgICAgaWYgcC5wYXJlbnQgYW5kIHAucGFyZW50ICE9ICJudWxsIjoKICAgICAgICAgICAgICAgIHBhcmVudCA9IGRpY3RfbmFtZXMuZ2V0KHAucGFyZW50KQogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICBpZiBwYXJlbnQ6CiAgICAgICAgICAgICAgICAgICAgcGFyZW50LmNoaWxkcmVuLmFkZChwKQogICAgICAgICAgICAgICAgICAgIHBlb3BsZV90b19yZW1vdmUgKz0gW3BdCiAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgZm9yIHAgaW4gcGVvcGxlX3RvX3JlbW92ZToKICAgICAgICAgICAgc2VsZi5fcGVyc29ucy5yZW1vdmUocCkKICAgICAgICAgICAgCiAgICAgICAgcmV0dXJuCiAgICAKICAgIGRlZiBhc19qc29uKHNlbGYpOgogICAgICAgIHJldHVybiBqc29uLmR1bXBzKHNlbGYsIGluZGVudD00LCBjbHM9SnNvblBlb3BsZUVuY29kZXIpCiAgICAKICAgIEBzdGF0aWNtZXRob2QKICAgIGRlZiByZWFkKGZkKToKICAgICAgICB0b3JldCA9IFBlb3BsZSgpCiAgICAgICAgCiAgICAgICAgcmVhZGVyID0gY3N2LkRpY3RSZWFkZXIoZmQsIGRlbGltaXRlcj0nLCcpCiAgICAgICAgZm9yIHJvdyBpbiByZWFkZXI6CiAgICAgICAgICAgIHRvcmV0ICs9IFBlcnNvbigKICAgICAgICAgICAgICAgICAgICAgICAgcm93WyJuYW1lIl0sCiAgICAgICAgICAgICAgICAgICAgICAgIHJvd1sic3VybmFtZSJdLAogICAgICAgICAgICAgICAgICAgICAgICByb3dbInBhcmVudCJdKQogICAgICAgICAgICAgCiAgICAgICAgdG9yZXQuX3B1dF9jaGlsZHJlbl9pbnNpZGVfcGVyc29ucygpCiAgICAgICAgcmV0dXJuIHRvcmV0CiAgICAgICAgICAgICAgICAKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6Cgl3aXRoIFN0cmluZ0lPKHN5cy5zdGRpbi5yZWFkKCkpIGFzIHN0cmY6CgkJcHMgPSBQZW9wbGUucmVhZChzdHJmKQoKCXByaW50KCJQZW9wbGU6IikKCXByaW50KHBzKQoJcHJpbnQoIlxuSlNPTjoiKQoJcHJpbnQocHMuYXNfanNvbigpKQo=
bmFtZSxzdXJuYW1lLHBhcmVudApkaWFuZSx4eHgsbnVsbApib2IseHh4LGRpYW5lCmJyaWFuLHh4eCxib2IKY2xhaXJlLHh4eCxicmlhbgp0b20seHh4LGJyaWFuCmphbmUseHh4LHRvbQptaWtlLHh4eCx0b20K
name,surname,parent
diane,xxx,null
bob,xxx,diane
brian,xxx,bob
claire,xxx,brian
tom,xxx,brian
jane,xxx,tom
mike,xxx,tom
People:
xxx, diane (null) (xxx, bob (diane) (xxx, brian (bob) (xxx, claire (brian)
xxx, tom (brian) (xxx, jane (tom)
xxx, mike (tom)))))
JSON:
[
{
"name": "diane",
"surname": "xxx",
"kids": 1,
"children": [
[
{
"name": "bob",
"surname": "xxx",
"kids": 1,
"children": [
[
{
"name": "brian",
"surname": "xxx",
"kids": 2,
"children": [
[
{
"name": "claire",
"surname": "xxx",
"kids": 0,
"children": []
},
{
"name": "tom",
"surname": "xxx",
"kids": 2,
"children": [
[
{
"name": "jane",
"surname": "xxx",
"kids": 0,
"children": []
},
{
"name": "mike",
"surname": "xxx",
"kids": 0,
"children": []
}
]
]
}
]
]
}
]
]
}
]
]
}
]