Exemple #1
0
class Article(models.Model):
    ARTICLE_KINDS = {
        "b": _("блог"),
        "i": _("наші розслідування"),
        "f": _("FAQ (загальний)"),
        "r": _("FAQ (видалення)"),
    }

    photo = models.ImageField("Головна світлина",
                              blank=True,
                              upload_to="images")

    caption = models.TextField("Заголовок")
    header = models.TextField("Піздаголовок", blank=True)
    text = RedactorField("Текст", blank=True)

    publish = models.BooleanField("Опубліковано", default=False)
    date = models.DateField("Дата публікації")
    kind = models.CharField("Тип статті",
                            max_length=5,
                            default="b",
                            choices=ARTICLE_KINDS.items())
    proofs = GenericRelation(
        "RelationshipProof",
        verbose_name="Посилання, соціальні мережі та документи")

    related_persons = models.ManyToManyField("Person",
                                             verbose_name="Пов'язані особи",
                                             related_name="articles",
                                             blank=True)
    related_companies = models.ManyToManyField(
        "Company",
        verbose_name="Пов'язані компанії",
        related_name="articles",
        blank=True,
    )

    last_editor = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        verbose_name="Автор останньої зміни",
        blank=True,
        null=True,
    )

    last_modified = models.DateTimeField("Остання зміна", auto_now=True)

    def __unicode__(self):
        return "{} ({})".format(self.caption_uk, self.get_kind_display())

    def get_absolute_url(self):
        return reverse("article_details", kwargs={"article_id": self.pk})

    class Meta:
        verbose_name = "Стаття"
        verbose_name_plural = "Статті"
Exemple #2
0
class DeclarationExtra(models.Model):
    person = models.ForeignKey("Person", related_name="declaration_extras")

    date_confirmed = models.DateField("Дата",
                                      blank=True,
                                      null=True,
                                      db_index=True)

    date_confirmed_details = models.IntegerField(
        "точність",
        choices=((0, "Точна дата"), (1, "Рік та місяць"), (2, "Тільки рік")),
        default=0,
    )

    @property
    def date_confirmed_human(self):
        return render_date(self.date_confirmed, self.date_confirmed_details)

    section = models.IntegerField(
        "Розділ декларації",
        choices=(
            (0, _("Загальна сума сукупного доходу, гривні")),
            (1, _("Дарунки, призи, виграші")),
            (2, _("Земельні ділянки")),
            (3, _("Житлові будинки")),
            (4, _("Квартири")),
            (5, _("Інше нерухоме майно")),
            (6, _("Транспортні засоби")),
            (7, _("Вклади у банках")),
            (8, _("Фінансові зобов’язання")),
            (9, _("Інші активи")),
        ),
        default=0,
        db_index=True,
    )

    note = RedactorField("Текст")
    address = RedactorField("Адреса", blank=True)
    country = models.ForeignKey("Country", blank=True)

    class Meta:
        verbose_name = "Додаткова інформація про статки"
        verbose_name_plural = "Додаткова інформація про статки"
Exemple #3
0
class Person(models.Model, AbstractNode):
    _reasons_of_termination = (
        (1, _("Помер")),
        (2, _("Звільнився/склав повноваження")),
        (3, _("Пов'язана особа або член сім'ї - ПЕП помер")),
        (4, _("Пов'язана особа або член сім'ї - ПЕП припинив бути ПЕПом")),
        (5, _("Зміни у законодавстві що визначає статус ПЕПа")),
        (6,
         _("Зміни форми власності юр. особи посада в котрій давала статус ПЕПа"
           )),
    )

    _types_of_officials = (
        (1, _("Національний публічний діяч")),
        (2, _("Іноземний публічний діяч")),
        (3, _("Діяч, що виконуює значні функції в міжнародній організації")),
        (4, _("Пов'язана особа")),
        (5, _("Член сім'ї")),
    )

    last_name = models.CharField("Прізвище", max_length=40)
    first_name = models.CharField("Ім'я", max_length=40)
    patronymic = models.CharField("По батькові", max_length=40, blank=True)

    publish = models.BooleanField("Опублікувати", default=True)
    is_pep = models.BooleanField("Є PEPом", default=True)
    imported = models.BooleanField("Був імпортований з гугл-таблиці",
                                   default=False)

    photo = models.ImageField("Світлина", blank=True, upload_to="images")
    dob = models.DateField("Дата народження", blank=True, null=True)
    dob_details = models.IntegerField(
        "Дата народження: точність",
        choices=((0, "Точна дата"), (1, "Рік та місяць"), (2, "Тільки рік")),
        default=0,
    )

    city_of_birth = models.CharField("Місто народження",
                                     max_length=100,
                                     blank=True)

    related_countries = models.ManyToManyField(
        "Country",
        verbose_name="Пов'язані країни",
        through="Person2Country",
        related_name="people",
    )

    reputation_assets = RedactorField("Статки", blank=True)

    reputation_sanctions = RedactorField("Наявність санкцій", blank=True)
    reputation_crimes = RedactorField("Кримінальні провадження", blank=True)
    reputation_manhunt = RedactorField("Перебування у розшуку", blank=True)
    reputation_convictions = RedactorField("Наявність судимості", blank=True)

    related_persons = select2.fields.ManyToManyField(
        "self",
        through="Person2Person",
        symmetrical=False,
        ajax=True,
        search_field=(
            lambda q: Q(last_name__icontains=q) | Q(first_name__icontains=q)),
    )

    related_companies = models.ManyToManyField("Company",
                                               through="Person2Company")

    wiki = RedactorField("Вікі-стаття", blank=True)
    wiki_draft = RedactorField("Чернетка вікі-статті", blank=True)

    wiki_url = models.URLField("Посилання на вікі",
                               blank=True,
                               max_length=1023)

    names = models.TextField("Варіанти написання імені", blank=True)

    also_known_as = models.TextField("Інші імена", blank=True)

    type_of_official = models.IntegerField("Тип ПЕП",
                                           choices=_types_of_officials,
                                           blank=True,
                                           null=True)

    risk_category = models.CharField(
        "Рівень ризику",
        choices=(
            ("danger", _("Неприйнятно високий")),
            ("high", _("Високий")),
            ("medium", _("Середній")),
            ("low", _("Низький")),
        ),
        max_length=6,
        default="low",
    )

    title = models.CharField(max_length=255, blank=True)
    description = models.TextField(blank=True)

    hash = models.CharField("Хеш", max_length=40, blank=True)

    reason_of_termination = models.IntegerField(
        "Причина припинення статусу ПЕП",
        choices=_reasons_of_termination,
        blank=True,
        null=True,
    )

    termination_date = models.DateField(
        "Дата припинення статусу ПЕП",
        blank=True,
        null=True,
        help_text=
        "Вказується реальна дата зміни без врахування 3 років (реальна дата звільнення, тощо)",
    )
    termination_date_details = models.IntegerField(
        "Дата припинення статусу ПЕП: точність",
        choices=((0, "Точна дата"), (1, "Рік та місяць"), (2, "Тільки рік")),
        default=0,
    )

    last_change = models.DateTimeField("Дата останньої зміни сторінки профіля",
                                       blank=True,
                                       null=True)

    last_editor = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        verbose_name="Автор останньої зміни сторінки профілю",
        blank=True,
        null=True,
    )

    _last_modified = models.DateTimeField("Остання зміна",
                                          null=True,
                                          blank=True)

    inn = models.CharField(_("ІПН з публічних джерел"),
                           max_length=40,
                           null=True,
                           blank=True)
    inn_source = models.ForeignKey(
        "core.Document",
        verbose_name="Документ з котрого було отримано ІПН",
        default=None,
        blank=True,
        null=True,
        related_name="inns",
    )
    passport = models.CharField(_("Паспортні дані з публічних джерел"),
                                max_length=40,
                                null=True,
                                blank=True)
    passport_source = models.ForeignKey(
        "core.Document",
        verbose_name="Документ з котрого було отримано ІПН",
        default=None,
        blank=True,
        null=True,
        related_name="passports",
    )
    proofs = GenericRelation(
        "RelationshipProof",
        verbose_name="Посилання, соціальні мережі та документи")

    @staticmethod
    def autocomplete_search_fields():
        return ("id__iexact", "last_name__icontains", "first_name__icontains")

    def __unicode__(self):
        return "%s %s %s" % (self.last_name, self.first_name, self.patronymic)

    @property
    def date_of_birth(self):
        return render_date(self.dob, self.dob_details)

    @property
    def termination_date_human(self):
        return render_date(self.termination_date,
                           self.termination_date_details)

    @property
    def terminated(self):
        # (1, _("Помер")),
        # (2, _("Звільнився/склав повноваження")),
        # (3, _("Пов'язана особа або член сім'ї - ПЕП помер")),
        # (4, _("Пов'язана особа або член сім'ї - ПЕП припинив бути ПЕПом")),
        # (5, _("Зміни у законодавстві що визначає статус ПЕПа")),
        # (6, _("Зміни форми власності юр. особи посада в котрій давала статус ПЕПа")),

        if self.reason_of_termination in [1, 3]:
            return True

        if (self.reason_of_termination in [2, 4, 5, 6]
                and self.termination_date is not None):
            if (ceil_date(self.termination_date, self.termination_date_details)
                    + datetime.timedelta(days=3 * 365) <=
                    datetime.date.today()):
                return True

        return False

    @property
    def died(self):
        return self.reason_of_termination == 1

    def _last_workplace(self):
        # Looking for a most recent appointment that has at least one date set
        # It'll work in following three cases:
        # Case 1: date_finished=null, date_established is the most recent one
        # i.e person got appointed and still holds the office
        # else
        # Case 2: date_finished=is the most recent one
        # and the date_established is the most recent one or null
        # i.e person got appointed and then resigned.

        # Tricky part: null values in dates are getting on top of the list when
        # you are sorting in decreasing order. So without exclude clause this
        # query will return the positions without both dates on the top of the
        # list
        qs = (
            self.person2company_set.order_by(
                "-is_employee", "-date_finished", "-date_established").exclude(
                    date_finished__isnull=True,
                    date_established__isnull=True)  # AND!
            .exclude(relationship_type_uk="Клієнт банку").prefetch_related(
                "to_company").only(
                    "to_company__short_name_uk",
                    "to_company__name_uk",
                    "to_company__short_name_en",
                    "to_company__name_en",
                    "to_company__id",
                    "relationship_type_uk",
                    "relationship_type_en",
                    "date_finished",
                    "date_finished_details",
                    "from_person_id",
                    "id",
                ))

        if qs:
            return qs

        # If nothing is found we are going to return the position that
        # has finished date set to null or the most recent one.
        # In contrast with previous query it'll also return those positions
        # where date_finished and date_established == null.
        qs = (self.person2company_set.order_by(
            "-is_employee",
            "-date_finished").prefetch_related("to_company").exclude(
                relationship_type_uk="Клієнт банку").only(
                    "to_company__short_name_uk",
                    "to_company__name_uk",
                    "to_company__short_name_en",
                    "to_company__name_en",
                    "to_company__id",
                    "relationship_type_uk",
                    "relationship_type_en",
                    "date_finished",
                    "date_finished_details",
                    "from_person_id",
                    "id",
                ))

        return qs

    @property
    def day_of_dismissal(self):
        dday = self._last_workplace().filter(is_employee=True).first()
        if dday:
            return render_date(dday.date_finished, dday.date_finished_details)
        else:
            return False

    def _last_workplace_from_declaration(self):
        return (Declaration.objects.filter(
            person=self,
            confirmed="a").exclude(doc_type="Кандидата на посаду").order_by(
                "-nacp_declaration",
                "-year").only("year", "office_en", "position_en", "office_uk",
                              "position_uk", "url")[:1])

    @property
    def last_workplace(self):
        qs = self._last_workplace()
        if qs:
            l = qs[0]
            return {
                "company": l.to_company.short_name_uk or l.to_company.name_uk,
                "company_id": l.to_company.pk,
                "position": l.relationship_type_uk,
            }
        else:
            qs = self._last_workplace_from_declaration()
            if qs:
                d = qs[0]
                return {
                    "company": d.office_uk,
                    "company_id": None,
                    "position": d.position_uk,
                }

        return ""

    # Fuuugly hack
    @property
    def last_workplace_en(self):
        qs = self._last_workplace()
        if qs:
            l = qs[0]

            return {
                "company": l.to_company.short_name_en or l.to_company.name_en,
                "company_id": l.to_company.pk,
                "position": l.relationship_type_en,
            }
        else:
            qs = self._last_workplace_from_declaration()
            if qs:
                d = qs[0]
                return {
                    "company": d.office_en,
                    "company_id": None,
                    "position": d.position_en,
                }

        return ""

    # Fuuugly hack
    @property
    def translated_last_workplace(self):
        # Add caching
        qs = self._last_workplace()
        if qs:
            l = qs[0]

            return {
                "company": l.to_company.short_name or l.to_company.name,
                "company_id": l.to_company.pk,
                "position": l.relationship_type,
            }
        else:
            qs = self._last_workplace_from_declaration()
            if qs:
                d = qs[0]
                return {
                    "company": d.office,
                    "company_id": None,
                    "position": d.position
                }

        return ""

    @property
    def workplaces(self):
        # Coalesce works by taking the first non-null value.  So we give it
        # a date far before any non-null values of last_active.  Then it will
        # naturally sort behind instances of Box with a non-null last_active
        # value.
        # djangoproject.com/en/1.8/ref/models/database-functions/#coalesce
        the_past = datetime.datetime.now() - datetime.timedelta(days=10 * 365)

        timeline = (self.person2company_set.prefetch_related(
            "to_company", "proofs", "proofs__proof_document").filter(
                is_employee=True).annotate(fixed_date_established=Coalesce(
                    "date_established", Value(the_past))).order_by(
                        "-fixed_date_established"))

        return timeline

    @property
    def assets(self):
        return self.person2company_set.prefetch_related(
            "to_company", "proofs", "proofs__proof_document").filter(
                is_employee=False,
                relationship_type_uk__in=(
                    "Член центрального статутного органу",
                    "Повірений у справах",
                    "Засновник/учасник",
                    "Колишній засновник/учасник",
                    "Бенефіціарний власник",
                    "Номінальний власник",
                    "Номінальний директор",
                    "Фінансові зв'язки",
                    "Секретар",
                    "Керуючий",
                    "Контролер",
                ),
            )

    @property
    def all_related_companies(self):
        companies = (self.person2company_set.prefetch_related(
            "to_company", "proofs", "proofs__proof_document").filter(
                is_employee=False).order_by("-pk"))

        banks = []
        rest = []
        all_connections = []
        for c in companies:
            if c.relationship_type_uk == "Клієнт банку":
                banks.append(c)
            else:
                rest.append(c)

            all_connections.append(c)

        return {"banks": banks, "rest": rest, "all": all_connections}

    @property
    def all_related_persons(self):
        related_persons = [
            (i.to_relationship_type, i.from_relationship_type,
             deepcopy(i.to_person), i)
            for i in self.to_persons.prefetch_related(
                "to_person", "proofs", "proofs__proof_document").defer(
                    "to_person__reputation_assets",
                    "to_person__reputation_sanctions",
                    "to_person__reputation_crimes",
                    "to_person__reputation_manhunt",
                    "to_person__reputation_convictions",
                    "to_person__wiki",
                    "to_person__names",
                    "to_person__hash",
                )
        ] + [(
            i.from_relationship_type,
            i.to_relationship_type,
            deepcopy(i.from_person),
            i,
        ) for i in self.from_persons.prefetch_related(
            "from_person", "proofs", "proofs__proof_document").defer(
                "from_person__reputation_assets",
                "from_person__reputation_sanctions",
                "from_person__reputation_crimes",
                "from_person__reputation_manhunt",
                "from_person__reputation_convictions",
                "from_person__wiki",
                "from_person__names",
                "from_person__hash",
            )]

        res = {"family": [], "personal": [], "business": [], "all": []}

        for rtp, rrtp, p, rel in related_persons:
            p.rtype = rtp
            p.reverse_rtype = rrtp
            p.connection = rel
            p.category = ""

            if rtp in ["особисті зв'язки"]:
                p.category = "personal"
                res["personal"].append(p)
            elif rtp in ["ділові зв'язки"]:
                p.category = "business"
                res["business"].append(p)
            else:
                p.category = "family"
                res["family"].append(p)

            res["all"].append(p)

        return res

    @property
    def parsed_names(self):
        return filter(None, self.names.split("\n"))

    @property
    def full_name(self):
        return ("%s %s %s" %
                (self.first_name, self.patronymic, self.last_name)).replace(
                    "  ", " ").strip()

    @property
    def short_name(self):
        return ("%s %s" % (self.first_name, self.last_name)).replace(
            "  ", " ").strip()

    @property
    def full_name_en(self):
        return ("%s %s %s" % (self.first_name_en, self.patronymic_en,
                              self.last_name_en)).replace("  ", " ")

    def to_dict(self):
        """
        Convert Person model to an indexable presentation for ES.
        """
        d = model_to_dict(
            self,
            fields=[
                "id",
                "last_name",
                "first_name",
                "patronymic",
                "dob",
                "last_name_en",
                "first_name_en",
                "patronymic_en",
                "dob_details",
                "is_pep",
                "names",
                "wiki_uk",
                "wiki_en",
                "city_of_birth_uk",
                "city_of_birth_en",
                "reputation_sanctions_uk",
                "reputation_sanctions_en",
                "reputation_convictions_uk",
                "reputation_convictions_en",
                "reputation_assets_uk",
                "reputation_assets_en",
                "reputation_crimes_uk",
                "reputation_crimes_en",
                "reputation_manhunt_uk",
                "reputation_manhunt_en",
                "also_known_as_uk",
                "also_known_as_en",
                "last_change",
                "inn",
                "inn_source",
                "passport",
                "passport_source",
            ],
        )

        d["related_persons"] = [
            i.to_dict() for i in self.to_persons.prefetch_related("to_person")
        ] + [
            i.to_dict_reverse()
            for i in self.from_persons.prefetch_related("from_person")
        ]
        d["related_countries"] = [
            i.to_dict()
            for i in self.person2country_set.prefetch_related("to_country")
        ]
        d["related_companies"] = [
            i.to_company_dict()
            for i in self.person2company_set.prefetch_related("to_company")
        ]

        d["declarations"] = [
            i.to_dict()
            for i in Declaration.objects.filter(person=self, confirmed="a")
        ]

        manhunt_records = self.manhunt_records
        if manhunt_records:
            curr_lang = get_language()

            activate("uk")
            d["reputation_manhunt_uk"] = render_to_string(
                "_manhunt_records_uk.jinja",
                {"manhunt_records": manhunt_records
                 }) + (d["reputation_manhunt_uk"] or "")

            activate("en")
            d["reputation_manhunt_en"] = render_to_string(
                "_manhunt_records_en.jinja",
                {"manhunt_records": manhunt_records
                 }) + (d["reputation_manhunt_en"] or "")
            activate(curr_lang)

        d["inn_source"] = (settings.SITE_URL +
                           self.inn_source.doc.url if self.inn_source else "")
        d["passport_source"] = (settings.SITE_URL +
                                self.passport_source.doc.url
                                if self.passport_source else "")

        d["photo"] = settings.SITE_URL + self.photo.url if self.photo else ""
        d["photo_path"] = self.photo.name if self.photo else ""
        d["date_of_birth"] = self.date_of_birth
        d["terminated"] = self.terminated
        d["last_modified"] = self.last_modified
        d["died"] = self.died
        if d["terminated"]:
            d["reason_of_termination"] = self.get_reason_of_termination_display(
            )
            d["reason_of_termination_en"] = translate_into(
                self.get_reason_of_termination_display(), "en")
            d["termination_date_human"] = self.termination_date_human

        last_workplace = self.last_workplace
        if last_workplace:
            d["last_workplace"] = last_workplace["company"]
            d["last_job_title"] = last_workplace["position"]
            d["last_job_id"] = last_workplace["company_id"]

            last_workplace_en = self.last_workplace_en
            d["last_workplace_en"] = last_workplace_en["company"]
            d["last_job_title_en"] = last_workplace_en["position"]

        d["type_of_official"] = self.get_type_of_official_display()

        d["type_of_official_en"] = translate_into(
            self.get_type_of_official_display(), "en")

        d["full_name"] = self.full_name
        d["full_name_en"] = self.full_name_en

        def generate_suggestions(last_name, first_name, patronymic, *args):
            if not last_name:
                return []

            return [
                {
                    "input": " ".join([last_name, first_name, patronymic]),
                    "weight": 5
                },
                {
                    "input": " ".join([first_name, patronymic, last_name]),
                    "weight": 2
                },
                {
                    "input": " ".join([first_name, last_name]),
                    "weight": 2
                },
            ]

        input_variants = [
            generate_suggestions(d["last_name"], d["first_name"],
                                 d["patronymic"])
        ]

        input_variants += list(
            map(lambda x: generate_suggestions(*parse_fullname(x)),
                self.parsed_names))

        d["full_name_suggest"] = list(chain.from_iterable(input_variants))

        d["_id"] = d["id"]

        return d

    def get_absolute_url(self):
        return reverse("person_details", kwargs={"person_id": self.pk})

    def localized_url(self, locale):
        curr_lang = get_language()
        activate(locale)
        url = self.get_absolute_url()
        activate(curr_lang)
        return url

    @property
    def foreign_citizenship_or_registration(self):
        return self.person2country_set.prefetch_related("to_country").filter(
            relationship_type__in=["citizenship", "registered_in"])

    @property
    def all_related_countries(self):
        connections = self.person2country_set.prefetch_related("to_country")

        types = defaultdict(list)
        for c in connections:
            types[c.relationship_type].append(c)
        return types

    @property
    def url_uk(self):
        return settings.SITE_URL + self.localized_url("uk")

    def save(self, *args, **kwargs):
        if self.first_name_uk:
            self.first_name_en = self.first_name_en or translitua(
                self.first_name_uk)
        else:
            self.first_name_en = ""

        if self.last_name_uk:
            self.last_name_en = self.last_name_en or translitua(
                self.last_name_uk)
        else:
            self.last_name_en = ""

        if self.patronymic_uk:
            self.patronymic_en = self.patronymic_en or translitua(
                self.patronymic_uk)
        else:
            self.patronymic_en = ""

        if self.also_known_as_uk:
            self.also_known_as_en = translitua(self.also_known_as_uk)
        else:
            self.also_known_as_en = ""

        if self.city_of_birth_uk and not self.city_of_birth_en:
            t = Ua2EnDictionary.objects.filter(
                term__iexact=lookup_term(self.city_of_birth_uk)).first()

            if t and t.translation:
                self.city_of_birth_en = t.translation

        super(Person, self).save(*args, **kwargs)

    @cached(timeout=60 * 2)
    def get_declarations(self):
        decls = Declaration.objects.filter(person=self,
                                           confirmed="a").order_by(
                                               "-year", "-nacp_declaration")

        corrected = []
        res = []
        # Filtering out original declarations, if there are
        # also corrected one
        for d in decls:
            if not d.nacp_declaration:
                continue

            if d.source["intro"].get("corrected"):
                corrected.append((d.year, d.source["intro"].get("doc_type")))

        for d in decls:
            if d.nacp_declaration and not d.source["intro"].get("corrected"):
                if (d.year, d.source["intro"].get("doc_type")) in corrected:
                    continue

            res.append(d)

        return res

    def get_charts_data(self):
        def cleanse(val):
            try:
                return float(unicode(val))
            except ValueError:
                return None

        decls = self.get_declarations()

        incomes = [
            [
                unicode(ugettext_lazy('Рік')),
                unicode(ugettext_lazy('Доходи декларанта')),
                unicode(ugettext_lazy('Доходи родини')),
                unicode(ugettext_lazy('Витрати декларанта'))
            ],
        ]

        assets = [[
            unicode(ugettext_lazy('Рік')),
            unicode(ugettext_lazy('Декларант')),
            unicode(ugettext_lazy('Родина'))
        ]]

        for d in decls[::-1]:
            income = d.get_income()
            incomes.append([
                unicode(income["year"]),
                cleanse(
                    convert_curr(income["income_of_declarant"],
                                 income["year"])),
                cleanse(
                    convert_curr(income["income_of_family"], income["year"])),
                cleanse(
                    convert_curr(income["expenses_of_declarant"],
                                 income["year"])),
            ])

            asset = d.get_assets()
            assets.append([
                unicode(asset["year"]),
                cleanse(
                    convert_curr(asset["total_uah"]["declarant"],
                                 asset["year"])),
                cleanse(
                    convert_curr(asset["total_uah"]["family"], asset["year"]))
            ])

        return {
            "incomes": incomes,
            "assets": assets,
        }

    def get_node(self):
        res = super(Person, self).get_node()

        node = {
            "is_pep": self.is_pep,
            "type_of_official": self.type_of_official or 0,
            "reason_of_termination": self.reason_of_termination or 0,
            "is_dead": self.reason_of_termination in [1, 3],
        }

        curr_lang = get_language()
        for lang in settings.LANGUAGE_CODES:
            activate(lang)
            node.update({
                "name_{}".format(lang):
                self.short_name,
                "full_name_{}".format(lang):
                self.full_name,
                "kind_{}".format(lang):
                unicode(
                    ugettext_lazy(self.get_type_of_official_display() or ""))
            })

            last_workplace = self.translated_last_workplace
            if last_workplace:
                last_workplace = "{position} @ {company}".format(
                    **last_workplace)
            else:
                last_workplace = ""

            node["description_{}".format(lang)] = last_workplace

        activate(curr_lang)
        res["data"].update(node)
        del res["data"]["connections"]
        del res["data"]["description"]
        del res["data"]["kind"]
        del res["data"]["url"]
        del res["data"]["id"]
        del res["data"]["details"]

        return res

    # temporary hack to keep old viz afloat
    def get_node_old(self):
        res = super(Person, self).get_node()

        node = {
            "name":
            self.short_name,
            "full_name":
            self.full_name,
            "is_pep":
            self.is_pep,
            "type_of_official":
            self.type_of_official,
            "reason_of_termination":
            self.reason_of_termination,
            "is_dead":
            self.reason_of_termination in [1, 3],
            "kind":
            unicode(ugettext_lazy(self.get_type_of_official_display() or ""))
        }
        last_workplace = self.translated_last_workplace

        if last_workplace:
            node["description"] = "{position} @ {company}".format(
                **last_workplace)

        res["data"].update(node)

        return res

    def get_node_info(self, with_connections=False):
        this_node = self.get_node_old()
        nodes = [this_node]
        edges = []
        all_connected = set()

        # Because of a complicated logic here we are piggybacking on
        # existing method that handles both directions of relations
        for p in self.all_related_persons["all"]:
            child_node_id = p.get_node_id()

            if with_connections:
                child_node = p.get_node_info(False)

                nodes += child_node["nodes"]
                edges += child_node["edges"]

                edges.append({
                    "data": {
                        "relation":
                        unicode(ugettext_lazy(p.rtype)),
                        "model":
                        p.connection._meta.model_name,
                        "pk":
                        p.connection.pk,
                        "id":
                        "{}-{}".format(p.connection._meta.model_name,
                                       p.connection.pk),
                        "share":
                        0,
                        "source":
                        this_node["data"]["id"],
                        "target":
                        child_node_id,
                        "is_latest":
                        True
                    }
                })

            all_connected.add(child_node_id)

        companies = self.person2company_set.prefetch_related(
            "to_company").exclude(relationship_type_uk="Клієнт банку")

        worked_for = {}
        connected_to = {}

        if with_connections:
            for c in companies:
                c.is_latest = False

                child_node_id = c.to_company.get_node_id()

                if c.is_employee:
                    bucket = worked_for
                else:
                    bucket = connected_to

                if child_node_id not in bucket:
                    bucket[child_node_id] = c
                else:
                    compare_with = bucket[child_node_id]

                    # When comparing two connections
                    if c.date_finished is not None or c.date_established is not None:
                        # Candidate with date_finished and date_established not set looses
                        if compare_with.date_finished is None and compare_with.date_established is None:
                            bucket[child_node_id] = c
                        else:
                            dt_now = (datetime.datetime.now() +
                                      datetime.timedelta(days=7)).date()

                            a_date_established = compare_with.date_established or dt_now
                            b_date_established = c.date_established or dt_now

                            a_date_finished = compare_with.date_finished or dt_now
                            b_date_finished = c.date_finished or dt_now

                            # Candidate with later date finished or open date_finished wins
                            if b_date_finished > a_date_finished:
                                bucket[child_node_id] = c
                            elif b_date_finished == a_date_finished:
                                # if both date finished are the same (for example two connections has open end)
                                # those with latest date_established wins
                                if b_date_established > a_date_established:
                                    bucket[child_node_id] = c

            for bucket in [worked_for, connected_to]:
                for c in bucket.values():
                    c.is_latest = True

        for c in companies:
            child_node_id = c.to_company.get_node_id()

            if with_connections:
                child_node = c.to_company.get_node_info(False)
                nodes += child_node["nodes"]
                edges += child_node["edges"]

                edges.append({
                    "data": {
                        "relation": unicode(c.relationship_type),
                        "model": c._meta.model_name,
                        "pk": c.pk,
                        "id": "{}-{}".format(c._meta.model_name, c.pk),
                        "source": this_node["data"]["id"],
                        "share": float(c.share or 0),
                        "target": child_node_id,
                        "is_latest": c.is_latest
                    }
                })

            all_connected.add(child_node_id)

        this_node["data"]["all_connected"] = list(all_connected)
        return {"edges": edges, "nodes": nodes}

    @property
    def manhunt_records(self):
        return [{
            "last_updated_from_dataset":
            rec.last_updated_from_dataset,
            "lost_date":
            dt_parse(rec.matched_json["LOST_DATE"], yearfirst=True),
            "articles_uk":
            rec.matched_json["ARTICLE_CRIM"],
            "articles_en":
            rec.matched_json["ARTICLE_CRIM"].lower().replace(
                "ст.", "article ").replace("ч.", "pt. "),
        } for rec in self.adhoc_matches.filter(status="a",
                                               dataset_id="wanted_ia")]

    @property
    def last_modified(self):
        p2p_conn = Person2Person.objects.filter(
            Q(from_person=self)
            | Q(to_person=self)).aggregate(mm=Max("_last_modified"))["mm"]

        p2comp_conn = Person2Company.objects.filter(
            Q(from_person=self)).aggregate(mm=Max("_last_modified"))["mm"]

        p2cont_conn = Person2Country.objects.filter(
            Q(from_person=self)).aggregate(mm=Max("_last_modified"))["mm"]

        seq = list(
            filter(
                None,
                [
                    p2p_conn,
                    p2comp_conn,
                    p2cont_conn,
                    self.last_change,
                    self._last_modified,
                ],
            ))
        if seq:
            return max(seq)

    @property
    def external_links(self):
        social_networks = {
            "facebook.com": "Facebook",
            "twitter.com": "Twitter",
            "vk.com": "Vkontakte",
            "instagram.com": "Instagram",
            "ok.ru": "Odnoklassniki",
            "linkedin.com": "LinkedIn"
        }
        other_networks = {
            "ru.wikipedia.org": "Wikipedia",
            "en.wikipedia.org": "Wikipedia",
            "de.wikipedia.org": "Wikipedia",
            "uk.wikipedia.org": "Wikipedia",
        }

        res = {"social_networks": [], "other": []}

        for proof in self.proofs.all():
            if proof.proof:
                domain = urlparse(proof.proof).hostname
                if domain is None:
                    continue

                domain = domain.replace("www.", "").lower()

                if domain in social_networks:
                    res["social_networks"].append({
                        "type":
                        social_networks[domain],
                        "title":
                        social_networks[domain],
                        "url":
                        proof.proof
                    })
                else:
                    res["other"].append({
                        "type":
                        domain,
                        "title":
                        proof.proof_title
                        or other_networks.get(domain, domain),
                        "url":
                        proof.proof
                    })

        return res

    def clean(self):
        if self.inn is not None and self.inn_source is None:
            raise ValidationError({
                "inn":
                "Не можна вказувати ІПН не надавши документальне підтвердження",
                "inn_source":
                "Не можна вказувати ІПН не надавши документальне підтвердження",
            })

        if self.passport is not None and self.passport_source is None:
            raise ValidationError({
                "passport":
                "Не можна вказувати ІПН не надавши документальне підтвердження",
                "passport_source":
                "Не можна вказувати ІПН не надавши документальне підтвердження",
            })

    @property
    def all_documents(self):
        companies = self.all_related_companies
        persons = self.all_related_persons
        proofs = []
        proofs_by_cat = defaultdict(list)

        for p in persons["all"]:
            for proof in p.connection.proofs.all():
                if not proof.proof_document:
                    continue
                proofs.append(proof)

        for c in companies["all"]:
            for proof in c.proofs.all():
                if not proof.proof_document:
                    continue
                proofs.append(proof)

        seen = set()
        for proof in proofs + list(self.proofs.all()):
            if proof.proof_document_id not in seen:
                if not proof.proof_document:
                    continue

                proofs_by_cat[proof.proof_document.doc_type].append(proof)

                seen.add(proof.proof_document_id)

        proofs_by_cat["misc"] += proofs_by_cat["other"]
        del proofs_by_cat["other"]

        return OrderedDict((Document.DOC_TYPE_CHOICES[k], proofs_by_cat[k])
                           for k in Document.DOC_TYPE_CHOICES.keys())

    class Meta:
        verbose_name = "Фізична особа"
        verbose_name_plural = "Фізичні особи"

        index_together = [["last_name", "first_name"]]

        permissions = (
            ("export_persons", "Can export the dataset"),
            (
                "export_id_and_last_modified",
                "Can export the dataset with person id and date of last modification",
            ),
        )
Exemple #4
0
class Company(models.Model, AbstractNode):
    HEADS_CLASSIFIERS = (
        re.compile(r"^керівник$"),
        re.compile(r"^т\.\s*в\.\s*о\.\s*керівника$"),
        re.compile(r"^в\.\s*о\.\s*керівника$"),
        re.compile(r"^директор$"),
        re.compile(r"^в.о директора$"),
        re.compile(r"^генеральний директор$"),
        re.compile(r"^т\.\s*в\.\s*о\.\s*генерального директора$"),
        re.compile(r"^в\.\s*о\.\s*генерального директора$"),
        re.compile(r"^начальник$"),
        re.compile(r"^в\.\s*о\.\s*начальника$"),
        re.compile(r"^в\.\s*о\.\s*начальника$"),
        re.compile(r"^ліквідатор$"),
        re.compile(r"^прокурор області$"),
        re.compile(r"^генеральний прокурор$"),
        re.compile(r"^надзвичайний і повноважний посол$"),
        re.compile(r"^прем’єр-міністр$"),
        re.compile(r"^президент$"),
        re.compile(r"^т\.\s*в\.\s*о\.\s*президента$"),
        re.compile(r"^в\.\s*о\.\s*президента$"),
        re.compile(r"^президент$"),
        re.compile(r"^підписант$"),
        re.compile(r"^номінальний директор$"),
        re.compile(r"міністр"),
        re.compile(r"^т\.\s*в\.\s*о\.\s*міністра$"),
        re.compile(r"^в\.\s*о\.\s*міністра"),
        re.compile(r"^голова$"),
        re.compile(r"^т\.\s*в\.\s*о\.\s*голови$"),
        re.compile(r"^в\.\s*о\.\s*голови$"),
        re.compile(r"^керуючий"),
        re.compile(r"^керуючий$"),
    )

    _status_choices = {
        0: _("інформація відсутня"),
        1: _("зареєстровано"),
        2: _("припинено"),
        3: _("в стані припинення"),
        4: _("зареєстровано, свідоцтво про державну реєстрацію недійсне"),
        5: _("порушено справу про банкрутство"),
        6: _("порушено справу про банкрутство (санація)"),
        7: _("розпорядження майном"),
        8: _("ліквідація"),
    }

    name = models.CharField("Повна назва", max_length=512)
    short_name = models.CharField("Скорочена назва",
                                  max_length=200,
                                  blank=True)

    also_known_as = models.TextField("Назви іншими мовами або варіації",
                                     blank=True)

    publish = models.BooleanField("Опублікувати", default=True)
    founded = models.DateField("Дата створення", blank=True, null=True)
    founded_details = models.IntegerField(
        "Дата створення: точність",
        choices=((0, "Точна дата"), (1, "Рік та місяць"), (2, "Тільки рік")),
        default=0,
    )

    status = models.IntegerField("Поточний стан",
                                 choices=_status_choices.items(),
                                 default=0)
    closed_on = models.DateField("Дата припинення", blank=True, null=True)
    closed_on_details = models.IntegerField(
        "Дата припинення: точність",
        choices=((0, "Точна дата"), (1, "Рік та місяць"), (2, "Тільки рік")),
        default=0,
    )

    @property
    def founded_human(self):
        return render_date(self.founded, self.founded_details)

    state_company = models.BooleanField("Керівник — ПЕП", default=False)

    legal_entity = models.BooleanField("Юрособа", default=True)

    edrpou = models.CharField("ЄДРПОУ", max_length=50, blank=True)

    zip_code = models.CharField("Індекс", max_length=20, blank=True)
    city = models.CharField("Місто", max_length=255, blank=True)
    street = models.CharField("Вулиця", max_length=100, blank=True)
    appt = models.CharField("№ будинку, офісу", max_length=50, blank=True)
    raw_address = models.TextField('"Сира" адреса', blank=True)

    wiki = RedactorField("Вікі-стаття", blank=True)

    other_founders = RedactorField("Інші засновники",
                                   help_text="Через кому, не PEP",
                                   blank=True)

    other_recipient = models.CharField("Бенефіціарій",
                                       help_text="Якщо не є PEPом",
                                       blank=True,
                                       max_length=200)

    other_owners = RedactorField("Інші власники",
                                 help_text="Через кому, не PEP",
                                 blank=True)

    other_managers = RedactorField("Інші керуючі",
                                   help_text="Через кому, не PEP",
                                   blank=True)

    bank_name = RedactorField("Фінансова інформація", blank=True)

    sanctions = RedactorField("Санкції", blank=True)

    related_companies = models.ManyToManyField("self",
                                               through="Company2Company",
                                               symmetrical=False)

    last_change = models.DateTimeField("Дата останньої зміни сторінки профіля",
                                       blank=True,
                                       null=True)

    last_editor = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        verbose_name="Автор останньої зміни сторінки профілю",
        blank=True,
        null=True,
    )

    public_office = models.BooleanField("Держ.орган", default=False)
    political_party = models.BooleanField("Партія", default=False)
    state_enterprise = models.BooleanField("Держ. власність", default=False)
    affiliated_with_pep = models.BooleanField("Пов'язана з ПЕП", default=False)
    bank = models.BooleanField("Банк", default=False)
    service_provider = models.BooleanField("Надавач послуг", default=False)
    _last_modified = models.DateTimeField("Остання зміна",
                                          null=True,
                                          blank=True)
    proofs = GenericRelation(
        "RelationshipProof",
        verbose_name="Посилання, соціальні мережі та документи")

    @staticmethod
    def autocomplete_search_fields():
        return ("id__iexact", "short_name__icontains", "name__icontains",
                "also_known_as__icontains")

    def __unicode__(self):
        return self.short_name or self.name

    def to_dict(self):
        d = model_to_dict(
            self,
            fields=[
                "id",
                "name_uk",
                "short_name_uk",
                "name_en",
                "short_name_en",
                "state_company",
                "edrpou",
                "wiki",
                "city",
                "street",
                "other_founders",
                "other_recipient",
                "other_owners",
                "other_managers",
                "bank_name",
                "also_known_as",
            ],
        )

        d["related_persons"] = [
            i.to_person_dict()
            for i in self.from_persons.prefetch_related("from_person")
        ]

        d["related_countries"] = [
            i.to_dict()
            for i in self.from_countries.prefetch_related("to_country")
        ]

        d["related_companies"] = [
            i.to_dict()
            for i in self.to_companies.prefetch_related("to_company")
        ] + [
            i.to_dict_reverse()
            for i in self.from_companies.prefetch_related("from_company")
        ]

        d["status"] = self.get_status_display()
        d["status_en"] = translate_into(self.get_status_display())
        d["founded"] = self.founded_human
        d["category"] = self.category
        d["closed"] = self.closed_on_human
        d["last_modified"] = self.last_modified

        suggestions = []
        names = set([
            d["name_uk"],
            d["short_name_uk"],
        ])

        for name in list(names):
            for ua_table in ALL_UKRAINIAN:
                names.add(translit(name, ua_table))

        names.add(d["name_en"])
        names.add(d["short_name_en"])

        for field in names:
            if not field:
                continue

            chunks = list(
                map(lambda x: x.strip("'\",.-“”«»"), field.split(" ")))

            for i in xrange(len(chunks)):
                variant = copy(chunks)
                variant = [variant[i]] + variant[:i] + variant[i + 1:]
                suggestions.append(" ".join(variant))

        if self.edrpou:
            edrpou_chunks = list(
                filter(
                    None,
                    map(
                        unicode.strip,
                        re.split("([a-z]+)", self.edrpou, flags=re.IGNORECASE),
                    ),
                ))

            suggestions += edrpou_chunks
            suggestions.append(self.edrpou.lstrip("0"))

            if self.edrpou.isdigit():
                suggestions.append(self.edrpou.rjust(8, "0"))

            d["code_chunks"] = edrpou_chunks

        d["name_suggest"] = [{"input": x} for x in set(suggestions)]

        d["name_suggest_output"] = d["short_name_uk"] or d["name_uk"]
        d["name_suggest_output_en"] = d["short_name_en"] or d["name_en"]

        d["_id"] = d["id"]

        return d

    def save(self, *args, **kwargs):
        if not self.name_en:
            t = Ua2EnDictionary.objects.filter(
                term__iexact=lookup_term(self.name_uk)).first()

            if t and t.translation:
                self.name_en = t.translation

        if not self.short_name_en:
            t = Ua2EnDictionary.objects.filter(
                term__iexact=lookup_term(self.short_name_uk)).first()

            if t and t.translation:
                self.short_name_en = t.translation

        edrpou = self.edrpou or ""
        if " " in edrpou and edrpou.strip() and ":" not in edrpou:
            self.edrpou = self.edrpou.replace(" ", "")

        super(Company, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse("company_details", kwargs={"company_id": self.pk})

    def localized_url(self, locale):
        curr_lang = get_language()
        activate(locale)
        url = self.get_absolute_url()
        activate(curr_lang)
        return url

    @property
    def url_uk(self):
        return settings.SITE_URL + self.localized_url("uk")

    @property
    def all_related_persons(self):
        related_persons = [
            (i.relationship_type_uk, deepcopy(i.from_person), i)
            for i in self.from_persons.prefetch_related("from_person").defer(
                "from_person__reputation_assets",
                "from_person__reputation_crimes",
                "from_person__reputation_manhunt",
                "from_person__reputation_convictions",
                "from_person__wiki",
                "from_person__names",
                "from_person__hash",
            ).order_by("from_person__last_name_uk",
                       "from_person__first_name_uk")
        ]

        res = {
            "managers": [],
            "founders": [],
            "sanctions": [],
            "bank_customers": [],
            "rest": [],
            "all": []
        }

        for rtp, p, rel in related_persons:
            add_to_rest = True
            p.rtype = rtp
            p.connection = rel

            res["all"].append(p)

            if any(map(lambda x: x.search(rtp.lower()),
                       self.HEADS_CLASSIFIERS)):
                if (rel.date_finished and
                        ceil_date(rel.date_finished,
                                  rel.date_finished_details) <= date.today()):
                    add_to_rest = True
                else:
                    res["managers"].append(p)
                    add_to_rest = False

            elif rtp.lower() in [
                    "засновник",
                    "учасник",
                    "власник",
                    "бенефіціарний власник",
                    "номінальний власник",
                    "колишній засновник/учасник",
            ]:
                res["founders"].append(p)
                add_to_rest = False

            elif rtp.lower() in ["клієнт банку"]:
                res["bank_customers"].append(p)
                add_to_rest = False

            if p.reputation_sanctions:
                res["sanctions"].append(p)
                add_to_rest = False

            if add_to_rest:
                res["rest"].append(p)

        return res

    @property
    def all_related_countries(self):
        related_countries = [
            (i.relationship_type, deepcopy(i.to_country), i)
            for i in self.from_countries.prefetch_related("to_country")
        ]

        res = defaultdict(list)

        for rtp, p, rel in related_countries:
            p.rtype = rtp
            p.connection = rel

            if rtp == "registered_in":
                res[rtp].append(p)
            else:
                res["rest"].append(p)

        return res

    # TODO: Request in bulk in all_related_companies?
    @property
    def foreign_registration(self):
        return self.from_countries.prefetch_related("to_country").filter(
            relationship_type="registered_in")

    @property
    def all_related_companies(self):
        related_companies = [
            (i.relationship_type, deepcopy(i.to_company), i, True)
            for i in self.to_companies.prefetch_related("to_company").defer(
                "to_company__wiki",
                "to_company__other_founders",
                "to_company__other_recipient",
                "to_company__other_owners",
                "to_company__other_managers",
                "to_company__bank_name",
                "to_company__sanctions",
            )
        ] + [(i.reverse_relationship_type, deepcopy(i.from_company), i, False)
             for i in self.from_companies.prefetch_related(
                 "from_company").defer(
                     "from_company__wiki",
                     "from_company__other_founders",
                     "from_company__other_recipient",
                     "from_company__other_owners",
                     "from_company__other_managers",
                     "from_company__bank_name",
                     "from_company__sanctions",
                 )]

        res = {"founders": [], "rest": [], "banks": [], "all": []}

        for rtp, p, rel, direction in sorted(related_companies,
                                             key=lambda x: x[1].name_uk):
            p.rtype = rtp
            p.connection = rel
            p.direction = direction

            if rtp in [
                    "Засновник",
                    "Співзасновник",
                    "Колишній власник/засновник",
                    "Колишній співвласник/співзасновник",
            ]:
                res["founders"].append(p)
            elif rtp == "Клієнт банку":
                res["banks"].append(p)
            else:
                res["rest"].append(p)

            res["all"].append(p)

        return res

    def get_node(self):
        res = super(Company, self).get_node()

        node = {
            "description": self.edrpou,
            "state_company": self.state_company,
            "is_closed": bool(self.closed_on_human),
            "public_office": self.public_office,
            "political_party": self.political_party,
            "state_enterprise": self.state_enterprise,
            "affiliated_with_pep": self.affiliated_with_pep,
            "bank": self.bank,
            "service_provider": self.service_provider,
            "category": self.category,
        }

        curr_lang = get_language()
        for lang in settings.LANGUAGE_CODES:
            activate(lang)
            node.update({
                "name_{}".format(lang):
                self.short_name or self.name,
                "full_name_{}".format(lang):
                self.name,
                "kind_{}".format(lang):
                unicode(
                    ugettext_lazy("Державна компанія чи установа") if self.
                    state_company else ugettext_lazy("Приватна компанія"))
            })

        activate(curr_lang)

        res["data"].update(node)
        del res["data"]["connections"]
        del res["data"]["description"]
        del res["data"]["kind"]
        del res["data"]["url"]
        del res["data"]["id"]
        del res["data"]["details"]

        return res

    # temporary hack to keep old viz afloat
    def get_node_old(self):
        res = super(Company, self).get_node()

        node = {
            "name":
            self.short_name or self.name,
            "full_name":
            self.name,
            "description":
            self.edrpou,
            "state_company":
            self.state_company,
            "category":
            self.category,
            "is_closed":
            bool(self.closed_on_human),
            "kind":
            unicode(
                ugettext_lazy("Державна компанія чи установа") if self.
                state_company else ugettext_lazy("Приватна компанія")),
        }

        res["data"].update(node)

        return res

    def get_node_info(self, with_connections=False):
        this_node = self.get_node_old()
        nodes = [this_node]
        edges = []
        all_connected = set()

        # Because of a complicated logic here we are piggybacking on
        # existing method that handles both directions of relations
        for p in self.all_related_persons["all"]:
            if p.rtype.lower() in [_("клієнт банку")]:
                continue

            child_node_id = p.get_node_id()

            if with_connections:
                child_node = p.get_node_info(False)
                nodes += child_node["nodes"]
                edges += child_node["edges"]

                edges.append({
                    "data": {
                        "relation":
                        unicode(ugettext_lazy(p.rtype)),
                        "model":
                        p.connection._meta.model_name,
                        "pk":
                        p.connection.pk,
                        "id":
                        "{}-{}".format(p.connection._meta.model_name,
                                       p.connection.pk),
                        "share":
                        0,
                        "target":
                        this_node["data"]["id"],
                        "source":
                        child_node_id,
                        "is_latest":
                        True,
                    }
                })

            all_connected.add(child_node_id)

        for c in self.all_related_companies["all"]:
            child_node_id = c.get_node_id()

            if with_connections:
                child_node = c.get_node_info(False)
                nodes += child_node["nodes"]
                edges += child_node["edges"]
                if c.direction:
                    source = child_node_id
                    target = this_node["data"]["id"]
                else:
                    source = this_node["data"]["id"]
                    target = child_node_id

                edges.append({
                    "data": {
                        "relation":
                        unicode(ugettext_lazy(c.connection.relationship_type)),
                        "model":
                        c.connection._meta.model_name,
                        "pk":
                        c.connection.pk,
                        "id":
                        "{}-{}".format(c.connection._meta.model_name,
                                       c.connection.pk),
                        "source":
                        source,
                        "share":
                        float(c.connection.equity_part or 0),
                        "target":
                        target,
                        "is_latest":
                        True,
                    }
                })

            all_connected.add(child_node_id)

        this_node["data"]["all_connected"] = list(all_connected)
        return {"edges": edges, "nodes": nodes}

    @property
    def closed_on_human(self):
        return render_date(self.closed_on, self.closed_on_details)

    @property
    def last_modified(self):
        c2c_conn = Company2Company.objects.filter(
            Q(from_company=self)
            | Q(to_company=self)).aggregate(mm=Max("_last_modified"))["mm"]

        c2p_conn = Person2Company.objects.filter(to_company=self).aggregate(
            mm=Max("_last_modified"))["mm"]

        c2cont_conn = Company2Country.objects.filter(
            from_company=self).aggregate(mm=Max("_last_modified"))["mm"]

        seq = list(
            filter(
                None,
                [
                    c2c_conn,
                    c2p_conn,
                    c2cont_conn,
                    self.last_change,
                    self._last_modified,
                ],
            ))
        if seq:
            return max(seq)

    @property
    def category(self):
        if self.public_office:
            return "public_office"

        if self.political_party:
            return "political_party"
        if self.state_enterprise:
            return "state_enterprise"
        if self.affiliated_with_pep:
            return "affiliated_with_pep"
        if self.bank:
            return "bank"
        if self.bank:
            return "service_provider"

        return "none"

    @property
    def all_documents(self):
        companies = self.all_related_companies
        persons = self.all_related_persons
        proofs = []
        proofs_by_cat = defaultdict(list)

        for p in persons["all"]:
            for proof in p.connection.proofs.all():
                if not proof.proof_document:
                    continue
                proofs.append(proof)

        for c in companies["all"]:
            for proof in c.connection.proofs.all():
                if not proof.proof_document:
                    continue
                proofs.append(proof)

        seen = set()
        for proof in proofs + list(self.proofs.all()):
            if proof.proof_document_id not in seen:
                proofs_by_cat[proof.proof_document.doc_type].append(proof)

                seen.add(proof.proof_document_id)

        proofs_by_cat["misc"] += proofs_by_cat["other"]
        del proofs_by_cat["other"]

        return OrderedDict((Document.DOC_TYPE_CHOICES[k], proofs_by_cat[k])
                           for k in Document.DOC_TYPE_CHOICES.keys())

    objects = CompanyManager()

    class Meta:
        verbose_name = "Юридична особа"
        verbose_name_plural = "Юридичні особи"

        permissions = (("export_companies",
                        "Can export the dataset of companies"), )
Exemple #5
0
class Person2Person(AbstractRelationship):
    _relationships_explained = OrderedDict([
        (_("чоловік"), [_("дружина")]),
        (_("дружина"), [_("чоловік")]),
        (_("чоловік/дружина"), [_("чоловік/дружина")]),
        (_("батько"), [_("син"), _("дочка"),
                       _("син/дочка")]),
        (_("мати"), [_("син"), _("дочка"),
                     _("син/дочка")]),
        (_("батько/мати"), [_("син"), _("дочка"),
                            _("син/дочка")]),
        (_("син"), [_("батько"), _("мати"),
                    _("батько/мати")]),
        (_("дочка"), [_("батько"), _("мати"),
                      _("батько/мати")]),
        (_("син/дочка"), [_("батько"),
                          _("мати"), _("батько/мати")]),
        (_("вітчим"), [_("пасинок"), _("падчерка")]),
        (_("мачуха"), [_("пасинок"), _("падчерка")]),
        (_("пасинок"), [_("вітчим"), _("мачуха")]),
        (_("падчерка"), [_("вітчим"), _("мачуха")]),
        (_("рідний брат"), [_("рідна сестра"),
                            _("рідний брат")]),
        (_("рідна сестра"), [_("рідна сестра"),
                             _("рідний брат")]),
        (_("дід"), [_("внук"), _("внучка")]),
        (_("баба"), [_("внук"), _("внучка")]),
        (_("прадід"), [_("правнук"), _("правнучка")]),
        (_("прабаба"), [_("правнук"), _("правнучка")]),
        (_("внук"), [_("дід"), _("баба")]),
        (_("внучка"), [_("дід"), _("баба")]),
        (_("правнук"), [_("прадід"), _("прабаба")]),
        (_("правнучка"), [_("прадід"), _("прабаба")]),
        (_("зять"), [_("теща"), _("тесть")]),
        (_("невістка"), [_("свекор"), _("свекруха")]),
        (_("тесть"), [_("зять")]),
        (_("теща"), [_("зять")]),
        (_("свекор"), [_("невістка")]),
        (_("свекруха"), [_("невістка")]),
        (_("усиновлювач"), [_("усиновлений")]),
        (_("усиновлений"), [_("усиновлювач")]),
        (_("опікун чи піклувальник"),
         [_("особа, яка перебуває під опікою або піклуванням")]),
        (_("особа, яка перебуває під опікою або піклуванням"),
         [_("опікун чи піклувальник")]),
        (_("особи, які спільно проживають"),
         [_("особи, які спільно проживають")]),
        (_("пов'язані спільним побутом і мають взаємні права та обов'язки"),
         [_("пов'язані спільним побутом і мають взаємні права та обов'язки")]),
        (_("ділові зв'язки"), [_("ділові зв'язки")]),
        (_("особисті зв'язки"), [_("особисті зв'язки")]),
    ])

    from_person = models.ForeignKey("Person",
                                    verbose_name="Персона 1",
                                    related_name="to_persons")
    to_person = models.ForeignKey("Person",
                                  verbose_name="Персона 2",
                                  related_name="from_persons")

    from_relationship_type = models.CharField(
        "Персона 1 є",
        choices=(zip(_relationships_explained.keys(),
                     map(_, _relationships_explained.keys()))),
        max_length=100,
        blank=True)

    to_relationship_type = models.CharField(
        "Персона 2 є",
        choices=(zip(_relationships_explained.keys(),
                     map(_, _relationships_explained.keys()))),
        max_length=100,
        blank=True)

    relationship_details = RedactorField("Детальний опис зв'язку", blank=True)

    declarations = ArrayField(
        models.IntegerField(),
        verbose_name="Декларації, що підтверджують зв'язок",
        null=True,
        blank=True)

    def __unicode__(self):
        return "%s (%s) -> %s (%s)" % (
            self.from_person, self.get_from_relationship_type_display(),
            self.to_person, self.get_to_relationship_type_display())

    def to_dict(self):
        """
        Convert link between two persons into indexable presentation.
        """
        return {
            "date_established":
            self.date_established_human,
            "date_finished":
            self.date_finished_human,
            "date_confirmed":
            self.date_confirmed_human,
            "relationship_type":
            self.to_relationship_type,
            "relationship_type_en":
            translate_into(self.to_relationship_type),
            "is_pep":
            self.to_person.is_pep,
            "person_uk":
            "%s %s %s" %
            (self.to_person.first_name_uk, self.to_person.patronymic_uk,
             self.to_person.last_name_uk),
            "person_en":
            "%s %s %s" %
            (self.to_person.first_name_en, self.to_person.patronymic_en,
             self.to_person.last_name_en),
        }

    def to_dict_reverse(self):
        """
        Convert back link between two persons to indexable presentation.
        """
        return {
            "date_established":
            self.date_established_human,
            "date_finished":
            self.date_finished_human,
            "date_confirmed":
            self.date_confirmed_human,
            "relationship_type":
            self.from_relationship_type,
            "relationship_type_en":
            translate_into(self.from_relationship_type),
            "is_pep":
            self.from_person.is_pep,
            "person_uk":
            "%s %s %s" %
            (self.from_person.first_name_uk, self.from_person.patronymic_uk,
             self.from_person.last_name_uk),
            "person_en":
            "%s %s %s" %
            (self.from_person.first_name_en, self.from_person.patronymic_en,
             self.from_person.last_name_en),
        }

    class Meta:
        verbose_name = "Зв'язок з іншою персоною"
        verbose_name_plural = "Зв'язки з іншими персонами"