From 63506a41674285f0020075607488bcb0fc3a8723 Mon Sep 17 00:00:00 2001 From: "Darrell Malone Jr." Date: Fri, 27 Sep 2024 22:50:26 -0500 Subject: [PATCH] OAS Updates (#409) * OAS Updates - Add suffix to the officer record - Add record IDs to complaints and allegations - Add type and subtype to allegations * partners->sources * Update pydantic models * Typo --- oas/2.0/agencies.yaml | 7 +-- oas/2.0/complaints.yaml | 20 +++++-- oas/2.0/officers.yaml | 7 ++- oas/2.0/{partners.yaml => sources.yaml} | 72 ++++++++++++------------- oas/gen_pydantic.sh | 7 +++ oas/pydantic/agencies.py | 5 +- oas/pydantic/complaints.py | 25 ++++++--- oas/pydantic/officers.py | 16 +++--- oas/pydantic/sources.py | 62 +++++++++++++++++++++ 9 files changed, 158 insertions(+), 63 deletions(-) rename oas/2.0/{partners.yaml => sources.yaml} (77%) create mode 100755 oas/gen_pydantic.sh create mode 100644 oas/pydantic/sources.py diff --git a/oas/2.0/agencies.yaml b/oas/2.0/agencies.yaml index 0671b53b6..86b98bf4b 100644 --- a/oas/2.0/agencies.yaml +++ b/oas/2.0/agencies.yaml @@ -264,9 +264,6 @@ components: BaseAgency: type: "object" properties: - uid: - type: "string" - description: "Unique identifier for the agency" name: type: "string" description: "Name of the agency" @@ -323,8 +320,8 @@ components: - type: "object" properties: uid: - type: "string" - description: "Unique identifier for the agency" + type: string + description: Unique identifier for the agency officers_url: type: "string" description: "URL to get a list of officers for this agency" diff --git a/oas/2.0/complaints.yaml b/oas/2.0/complaints.yaml index 0ef4d923e..a58e5d607 100644 --- a/oas/2.0/complaints.yaml +++ b/oas/2.0/complaints.yaml @@ -125,6 +125,9 @@ components: type: "object" description: "Base complaint object" properties: + record_id: + type: string + description: The ID that was given to this complaint by the orginal source of the data. source_details: $ref: '#/components/schemas/SourceDetails' category: @@ -137,7 +140,7 @@ components: recieved_date: type: "string" format: "date-time" - description: "The date and time the complaint was received by the reporting partner." + description: "The date and time the complaint was received by the reporting source." closed_date: type: "string" format: "date-time" @@ -189,7 +192,7 @@ components: properties: source_uid: type: "string" - description: "The UID of the partner that reported the complaint." + description: "The UID of the source that reported the complaint." civilian_review_board_uid: type: "string" description: "The UID of the civilian review board that reviewed the complaint." @@ -274,9 +277,9 @@ components: description: "Date and time the complaint was last updated." source: allOf: - - $ref: partners.yaml#/components/schemas/Partner + - $ref: sources.yaml#/components/schemas/Source - type: "object" - description: "The partner that reported the complaint." + description: "The source that reported the complaint." civilian_review_board: allOf: - $ref: '#/components/schemas/ReviewBoard' @@ -315,6 +318,9 @@ components: BaseAllegation: type: "object" properties: + record_id: + type: string + description: The ID that was given to this allegation by the orginal source of the data. complaintant: allOf: - $ref: "#/components/schemas/Civilian" @@ -323,6 +329,12 @@ components: allegation: type: "string" description: "The allegation made by the complaintant." + type: + type: string + description: The type of allegation. + sub_type: + type: string + description: The sub type of the allegation. recomended_finding: type: "string" description: "The finding recomended by the review board." diff --git a/oas/2.0/officers.yaml b/oas/2.0/officers.yaml index 9ea05a793..68f33a9c3 100644 --- a/oas/2.0/officers.yaml +++ b/oas/2.0/officers.yaml @@ -186,11 +186,11 @@ components: earliest_employment: type: "string" format: "date" - description: "The earliest date of employment" + description: "The earliest known date of employment" latest_employment: type: "string" format: "date" - description: "The latest date of employment" + description: "The latest known date of employment" badge_number: type: "string" description: "The badge number of the officer" @@ -270,6 +270,9 @@ components: last_name: type: "string" description: "Last name of the officer" + suffix: + type: "string" + description: "Suffix of the officer's name" ethnicity: type: "string" description: "The ethnicity of the officer" diff --git a/oas/2.0/partners.yaml b/oas/2.0/sources.yaml similarity index 77% rename from oas/2.0/partners.yaml rename to oas/2.0/sources.yaml index 740714f88..c1757aee4 100644 --- a/oas/2.0/partners.yaml +++ b/oas/2.0/sources.yaml @@ -1,6 +1,6 @@ openapi: "3.0.3" info: - title: "Partners" + title: "Sources" description: "API Description" version: "0.1.0" servers: @@ -17,15 +17,15 @@ x-readme: security: - bearerAuth: [] tags: - - name: "Partners" - description: "Partners API" + - name: "Sources" + description: "Sources API" paths: - /partners: + /sources: get: tags: - - "Partners" - summary: "Get all partners" - operationId: "getPartners" + - "Sources" + summary: "Get all sources" + operationId: "getSources" responses: "200": description: "Successful operation" @@ -37,8 +37,8 @@ paths: $ref: "#/components/schemas/PartnerList" post: tags: - - "Partners" - summary: "Create a new partner" + - "Sources" + summary: "Create a new source" operationId: "createPartner" requestBody: content: @@ -51,37 +51,37 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Partner' + $ref: '#/components/schemas/Source' '400': $ref: '../common/error.yaml#/components/responses/validationError' - /partners/{uid}: + /sources/{uid}: parameters: - name: uid in: path - description: UID of the partner + description: UID of the source required: true schema: type: string get: tags: - - "Partners" - summary: "Get Partner" + - "Sources" + summary: "Get Source" operationId: "getPartnerById" description: > - Returns a single partner. + Returns a single source. responses: '200': description: "Successful operation" content: application/json: schema: - $ref: "#/components/schemas/Partner" + $ref: "#/components/schemas/Source" '404': $ref: '../common/error.yaml#/components/responses/notFoundError' patch: tags: - - "Partners" - summary: "Update an existing partner" + - "Sources" + summary: "Update an existing source" operationId: "updatePartner" requestBody: content: @@ -94,26 +94,26 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Partner' + $ref: '#/components/schemas/Source' '400': $ref: '../common/error.yaml#/components/responses/validationError' '404': $ref: '../common/error.yaml#/components/responses/notFoundError' - /partners/{uid}/members: + /sources/{uid}/members: parameters: - name: uid in: path - description: UID of the partner + description: UID of the source required: true schema: type: string get: tags: - - Partners + - Sources summary: "Get all members" operationId: "getMembers" description: > - Returns a list of all users who are members of a partner. + Returns a list of all users who are members of a source. responses: '200': description: 'Successful operation' @@ -138,13 +138,13 @@ components: properties: name: type: "string" - description: "Name of the partner organization." + description: "Name of the source organization." url: type: "string" - description: "Website URL of the partner." + description: "Website URL of the source." contact_email: type: "string" - description: "Contact email for the partner organization." + description: "Contact email for the source organization." CreatePartner: allOf: - $ref: "#/components/schemas/BasePartner" @@ -155,20 +155,20 @@ components: UpdatePartner: allOf: - $ref: "#/components/schemas/BasePartner" - Partner: + Source: allOf: - $ref: "#/components/schemas/BasePartner" - type: "object" properties: uid: type: "string" - description: "Unique identifier for the partner." + description: "Unique identifier for the source." members: type: string - description: "Url to get all members of the partner." - reported_incidents: + description: "Url to get all members of the source." + reported_complaints: type: string - description: "Url to get all incidents reported by the partner." + description: "Url to get all complaints reported by the source." PartnerList: allOf: - $ref: '../common/pagination.yaml#/components/schemas/PaginatedResponse' @@ -177,13 +177,13 @@ components: results: type: "array" items: - $ref: "#/components/schemas/Partner" + $ref: "#/components/schemas/Source" MemberBase: type: "object" properties: - partner_uid: + source_uid: type: "string" - description: "Unique identifier for the partner." + description: "Unique identifier for the source." user_uid: type: "string" description: "Unique identifier for the user." @@ -210,7 +210,7 @@ components: date_joined: type: "string" format: "date-time" - description: "Date the user joined the partner organizaation." + description: "Date the user joined the source organizaation." AddMember: allOf: - $ref: "#/components/schemas/MemberBase" @@ -218,7 +218,7 @@ components: required: - role - user_uid - - partner_uid + - source_uid MemberList: allOf: - $ref: '../common/pagination.yaml#/components/schemas/PaginatedResponse' diff --git a/oas/gen_pydantic.sh b/oas/gen_pydantic.sh new file mode 100755 index 000000000..c90d86928 --- /dev/null +++ b/oas/gen_pydantic.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +python oas_to_pydantic.py 2.0/sources.yaml pydantic/sources.py +python oas_to_pydantic.py 2.0/complaints.yaml pydantic/complaints.py +python oas_to_pydantic.py 2.0/officers.yaml pydantic/officers.py +python oas_to_pydantic.py 2.0/agencies.yaml pydantic/agencies.py +python oas_to_pydantic.py 2.0/litigation.yaml pydantic/litigation.py \ No newline at end of file diff --git a/oas/pydantic/agencies.py b/oas/pydantic/agencies.py index ddaaf207b..b13867fff 100644 --- a/oas/pydantic/agencies.py +++ b/oas/pydantic/agencies.py @@ -3,7 +3,6 @@ class BaseAgency(BaseModel): - uid: Optional[str] = Field(None, description="Unique identifier for the agency") name: Optional[str] = Field(None, description="Name of the agency") hq_address: Optional[str] = Field(None, description="Address of the agency") hq_city: Optional[str] = Field(None, description="City of the agency") @@ -16,7 +15,6 @@ class BaseAgency(BaseModel): class CreateAgency(BaseAgency, BaseModel): - uid: Optional[str] = Field(None, description="Unique identifier for the agency") name: Optional[str] = Field(None, description="Name of the agency") hq_address: Optional[str] = Field(None, description="Address of the agency") hq_city: Optional[str] = Field(None, description="City of the agency") @@ -29,7 +27,6 @@ class CreateAgency(BaseAgency, BaseModel): class UpdateAgency(BaseAgency, BaseModel): - uid: Optional[str] = Field(None, description="Unique identifier for the agency") name: Optional[str] = Field(None, description="Name of the agency") hq_address: Optional[str] = Field(None, description="Address of the agency") hq_city: Optional[str] = Field(None, description="City of the agency") @@ -46,7 +43,6 @@ class AgencyList(PaginatedResponse, BaseModel): class Agency(BaseAgency, BaseModel): - uid: Optional[str] = Field(None, description="Unique identifier for the agency") name: Optional[str] = Field(None, description="Name of the agency") hq_address: Optional[str] = Field(None, description="Address of the agency") hq_city: Optional[str] = Field(None, description="City of the agency") @@ -56,6 +52,7 @@ class Agency(BaseAgency, BaseModel): phone: Optional[str] = Field(None, description="Phone number of the agency") email: Optional[str] = Field(None, description="Email of the agency") website_url: Optional[str] = Field(None, description="Website of the agency") + uid: Optional[str] = Field(None, description="Unique identifier for the agency") officers_url: Optional[str] = Field(None, description="URL to get a list of officers for this agency") units_url: Optional[str] = Field(None, description="URL to get a list of units for this agency") diff --git a/oas/pydantic/complaints.py b/oas/pydantic/complaints.py index af44d2bd6..8f96120ad 100644 --- a/oas/pydantic/complaints.py +++ b/oas/pydantic/complaints.py @@ -4,10 +4,11 @@ class BaseComplaint(BaseModel): """Base complaint object""" + record_id: Optional[str] = Field(None, description="The ID that was given to this complaint by the orginal source of the data.") source_details: Optional[SourceDetails] = None category: Optional[str] = Field(None, description="The category of the complaint.") incident_date: Optional[str] = Field(None, description="The date and time the incident occurred.") - recieved_date: Optional[str] = Field(None, description="The date and time the complaint was received by the reporting partner.") + recieved_date: Optional[str] = Field(None, description="The date and time the complaint was received by the reporting source.") closed_date: Optional[str] = Field(None, description="The date and time the complaint was closed.") location: Optional[Dict[str, Any]] = None reason_for_contact: Optional[str] = Field(None, description="The reason for the contact.") @@ -17,17 +18,18 @@ class BaseComplaint(BaseModel): class CreateComplaint(BaseComplaint, BaseModel): + record_id: Optional[str] = Field(None, description="The ID that was given to this complaint by the orginal source of the data.") source_details: Optional[SourceDetails] = None category: Optional[str] = Field(None, description="The category of the complaint.") incident_date: Optional[str] = Field(None, description="The date and time the incident occurred.") - recieved_date: Optional[str] = Field(None, description="The date and time the complaint was received by the reporting partner.") + recieved_date: Optional[str] = Field(None, description="The date and time the complaint was received by the reporting source.") closed_date: Optional[str] = Field(None, description="The date and time the complaint was closed.") location: Optional[Dict[str, Any]] = None reason_for_contact: Optional[str] = Field(None, description="The reason for the contact.") outcome_of_contact: Optional[str] = Field(None, description="The outcome of the contact.") civilian_witnesses: Optional[List[Civilian]] = Field(None, description="The civilian witnesses associated with the complaint.") attachements: Optional[List[Attachemnts]] = Field(None, description="Documents and multimeida associated with the complaint.") - source_uid: Optional[str] = Field(None, description="The UID of the partner that reported the complaint.") + source_uid: Optional[str] = Field(None, description="The UID of the source that reported the complaint.") civilian_review_board_uid: Optional[str] = Field(None, description="The UID of the civilian review board that reviewed the complaint.") police_witnesses: Optional[List[str]] = Field(None, description="The UID of any police witnesses associated with the complaint.") allegations: Optional[List[CreateAllegation]] = Field(None, description="The allegations associated with the complaint.") @@ -36,10 +38,11 @@ class CreateComplaint(BaseComplaint, BaseModel): class UpdateComplaint(BaseComplaint, BaseModel): + record_id: Optional[str] = Field(None, description="The ID that was given to this complaint by the orginal source of the data.") source_details: Optional[SourceDetails] = None category: Optional[str] = Field(None, description="The category of the complaint.") incident_date: Optional[str] = Field(None, description="The date and time the incident occurred.") - recieved_date: Optional[str] = Field(None, description="The date and time the complaint was received by the reporting partner.") + recieved_date: Optional[str] = Field(None, description="The date and time the complaint was received by the reporting source.") closed_date: Optional[str] = Field(None, description="The date and time the complaint was closed.") location: Optional[Dict[str, Any]] = None reason_for_contact: Optional[str] = Field(None, description="The reason for the contact.") @@ -54,10 +57,11 @@ class UpdateComplaint(BaseComplaint, BaseModel): class Complaint(BaseComplaint, BaseModel): + record_id: Optional[str] = Field(None, description="The ID that was given to this complaint by the orginal source of the data.") source_details: Optional[SourceDetails] = None category: str = Field(..., description="The category of the complaint.") incident_date: str = Field(..., description="The date and time the incident occurred.") - recieved_date: str = Field(..., description="The date and time the complaint was received by the reporting partner.") + recieved_date: str = Field(..., description="The date and time the complaint was received by the reporting source.") closed_date: Optional[str] = Field(None, description="The date and time the complaint was closed.") location: Dict[str, Any] = ... reason_for_contact: Optional[str] = Field(None, description="The reason for the contact.") @@ -67,7 +71,7 @@ class Complaint(BaseComplaint, BaseModel): uid: str = Field(..., description="Unique identifier for the complaint.") created_at: str = Field(..., description="Date and time the complaint was created.") updated_at: str = Field(..., description="Date and time the complaint was last updated.") - source: Optional[Partner] = Field(None, description="The partner that reported the complaint.") + source: Optional[Source] = Field(None, description="The source that reported the complaint.") civilian_review_board: Optional[ReviewBoard] = Field(None, description="The civilian review board that reviewed the complaint.") police_witnesses: List[Officer] = Field(..., description="The police witnesses associated with the complaint.") allegations: List[Allegation] = Field(..., description="The allegations associated with the complaint.") @@ -80,8 +84,11 @@ class ComplaintList(PaginatedResponse, BaseModel): class BaseAllegation(BaseModel): + record_id: Optional[str] = Field(None, description="The ID that was given to this allegation by the orginal source of the data.") complaintant: Optional[Civilian] = Field(None, description="Demographic information of the individual who filed the complaint.") allegation: Optional[str] = Field(None, description="The allegation made by the complaintant.") + type: Optional[str] = Field(None, description="The type of allegation.") + sub_type: Optional[str] = Field(None, description="The sub type of the allegation.") recomended_finding: Optional[str] = Field(None, description="The finding recomended by the review board.") recomended_outcome: Optional[str] = Field(None, description="The outcome recomended by the review board.") finding: Optional[str] = Field(None, description="The legal finding.") @@ -89,8 +96,11 @@ class BaseAllegation(BaseModel): class CreateAllegation(BaseAllegation, BaseModel): + record_id: Optional[str] = Field(None, description="The ID that was given to this allegation by the orginal source of the data.") complaintant: Optional[Civilian] = Field(None, description="Demographic information of the individual who filed the complaint.") allegation: Optional[str] = Field(None, description="The allegation made by the complaintant.") + type: Optional[str] = Field(None, description="The type of allegation.") + sub_type: Optional[str] = Field(None, description="The sub type of the allegation.") recomended_finding: Optional[str] = Field(None, description="The finding recomended by the review board.") recomended_outcome: Optional[str] = Field(None, description="The outcome recomended by the review board.") finding: Optional[str] = Field(None, description="The legal finding.") @@ -99,8 +109,11 @@ class CreateAllegation(BaseAllegation, BaseModel): class Allegation(BaseAllegation, BaseModel): + record_id: Optional[str] = Field(None, description="The ID that was given to this allegation by the orginal source of the data.") complaintant: Optional[Civilian] = Field(None, description="Demographic information of the individual who filed the complaint.") allegation: Optional[str] = Field(None, description="The allegation made by the complaintant.") + type: Optional[str] = Field(None, description="The type of allegation.") + sub_type: Optional[str] = Field(None, description="The sub type of the allegation.") recomended_finding: Optional[str] = Field(None, description="The finding recomended by the review board.") recomended_outcome: Optional[str] = Field(None, description="The outcome recomended by the review board.") finding: Optional[str] = Field(None, description="The legal finding.") diff --git a/oas/pydantic/officers.py b/oas/pydantic/officers.py index 94f689048..1780acc7d 100644 --- a/oas/pydantic/officers.py +++ b/oas/pydantic/officers.py @@ -6,8 +6,8 @@ class BaseEmployment(BaseModel): officer_uid: Optional[str] = Field(None, description="The UID of the officer.") agency_uid: Optional[str] = Field(None, description="The UID of the agency the officer is employed by.") unit_uid: Optional[str] = Field(None, description="The UID of the unit the officer is assigned to.") - earliest_employment: Optional[str] = Field(None, description="The earliest date of employment") - latest_employment: Optional[str] = Field(None, description="The latest date of employment") + earliest_employment: Optional[str] = Field(None, description="The earliest known date of employment") + latest_employment: Optional[str] = Field(None, description="The latest known date of employment") badge_number: Optional[str] = Field(None, description="The badge number of the officer") highest_rank: Optional[str] = Field(None, description="The highest rank the officer has held during this employment.") commander: Optional[bool] = Field(None, description="Indicates that the officer commanded the unit during this employment.") @@ -17,8 +17,8 @@ class AddEmployment(BaseEmployment, BaseModel): officer_uid: Optional[str] = Field(None, description="The UID of the officer.") agency_uid: Optional[str] = Field(None, description="The UID of the agency the officer is employed by.") unit_uid: Optional[str] = Field(None, description="The UID of the unit the officer is assigned to.") - earliest_employment: Optional[str] = Field(None, description="The earliest date of employment") - latest_employment: Optional[str] = Field(None, description="The latest date of employment") + earliest_employment: Optional[str] = Field(None, description="The earliest known date of employment") + latest_employment: Optional[str] = Field(None, description="The latest known date of employment") badge_number: Optional[str] = Field(None, description="The badge number of the officer") highest_rank: Optional[str] = Field(None, description="The highest rank the officer has held during this employment.") commander: Optional[bool] = Field(None, description="Indicates that the officer commanded the unit during this employment.") @@ -37,8 +37,8 @@ class Employment(BaseEmployment, BaseModel): officer_uid: Optional[str] = Field(None, description="The UID of the officer.") agency_uid: Optional[str] = Field(None, description="The UID of the agency the officer is employed by.") unit_uid: Optional[str] = Field(None, description="The UID of the unit the officer is assigned to.") - earliest_employment: Optional[str] = Field(None, description="The earliest date of employment") - latest_employment: Optional[str] = Field(None, description="The latest date of employment") + earliest_employment: Optional[str] = Field(None, description="The earliest known date of employment") + latest_employment: Optional[str] = Field(None, description="The latest known date of employment") badge_number: Optional[str] = Field(None, description="The badge number of the officer") highest_rank: Optional[str] = Field(None, description="The highest rank the officer has held during this employment.") commander: Optional[bool] = Field(None, description="Indicates that the officer commanded the unit during this employment.") @@ -59,6 +59,7 @@ class BaseOfficer(BaseModel): first_name: Optional[str] = Field(None, description="First name of the officer") middle_name: Optional[str] = Field(None, description="Middle name of the officer") last_name: Optional[str] = Field(None, description="Last name of the officer") + suffix: Optional[str] = Field(None, description="Suffix of the officer's name") ethnicity: Optional[str] = Field(None, description="The ethnicity of the officer") gender: Optional[str] = Field(None, description="The gender of the officer") date_of_birth: Optional[str] = Field(None, description="The date of birth of the officer") @@ -69,6 +70,7 @@ class CreateOfficer(BaseOfficer, BaseModel): first_name: Optional[str] = Field(None, description="First name of the officer") middle_name: Optional[str] = Field(None, description="Middle name of the officer") last_name: Optional[str] = Field(None, description="Last name of the officer") + suffix: Optional[str] = Field(None, description="Suffix of the officer's name") ethnicity: Optional[str] = Field(None, description="The ethnicity of the officer") gender: Optional[str] = Field(None, description="The gender of the officer") date_of_birth: Optional[str] = Field(None, description="The date of birth of the officer") @@ -79,6 +81,7 @@ class UpdateOfficer(BaseOfficer, BaseModel): first_name: Optional[str] = Field(None, description="First name of the officer") middle_name: Optional[str] = Field(None, description="Middle name of the officer") last_name: Optional[str] = Field(None, description="Last name of the officer") + suffix: Optional[str] = Field(None, description="Suffix of the officer's name") ethnicity: Optional[str] = Field(None, description="The ethnicity of the officer") gender: Optional[str] = Field(None, description="The gender of the officer") date_of_birth: Optional[str] = Field(None, description="The date of birth of the officer") @@ -89,6 +92,7 @@ class Officer(BaseOfficer, BaseModel): first_name: Optional[str] = Field(None, description="First name of the officer") middle_name: Optional[str] = Field(None, description="Middle name of the officer") last_name: Optional[str] = Field(None, description="Last name of the officer") + suffix: Optional[str] = Field(None, description="Suffix of the officer's name") ethnicity: Optional[str] = Field(None, description="The ethnicity of the officer") gender: Optional[str] = Field(None, description="The gender of the officer") date_of_birth: Optional[str] = Field(None, description="The date of birth of the officer") diff --git a/oas/pydantic/sources.py b/oas/pydantic/sources.py new file mode 100644 index 000000000..22cc96c80 --- /dev/null +++ b/oas/pydantic/sources.py @@ -0,0 +1,62 @@ +from pydantic import BaseModel, Field +from typing import List, Optional, Dict, Any, Union + + +class BasePartner(BaseModel): + name: Optional[str] = Field(None, description="Name of the source organization.") + url: Optional[str] = Field(None, description="Website URL of the source.") + contact_email: Optional[str] = Field(None, description="Contact email for the source organization.") + + +class CreatePartner(BasePartner, BaseModel): + name: Optional[str] = Field(None, description="Name of the source organization.") + url: Optional[str] = Field(None, description="Website URL of the source.") + contact_email: Optional[str] = Field(None, description="Contact email for the source organization.") + + +class UpdatePartner(BasePartner, BaseModel): + name: Optional[str] = Field(None, description="Name of the source organization.") + url: Optional[str] = Field(None, description="Website URL of the source.") + contact_email: Optional[str] = Field(None, description="Contact email for the source organization.") + + +class Source(BasePartner, BaseModel): + name: Optional[str] = Field(None, description="Name of the source organization.") + url: Optional[str] = Field(None, description="Website URL of the source.") + contact_email: Optional[str] = Field(None, description="Contact email for the source organization.") + uid: Optional[str] = Field(None, description="Unique identifier for the source.") + members: Optional[str] = Field(None, description="Url to get all members of the source.") + reported_complaints: Optional[str] = Field(None, description="Url to get all complaints reported by the source.") + + +class PartnerList(PaginatedResponse, BaseModel): + results: Optional[List[Source]] = None + + +class MemberBase(BaseModel): + source_uid: Optional[str] = Field(None, description="Unique identifier for the source.") + user_uid: Optional[str] = Field(None, description="Unique identifier for the user.") + role: Optional[str] = Field(None, description="Role of the user.") + is_active: Optional[bool] = Field(None, description="Whether the user is active.") + + +class Member(MemberBase, BaseModel): + source_uid: Optional[str] = Field(None, description="Unique identifier for the source.") + user_uid: Optional[str] = Field(None, description="Unique identifier for the user.") + role: Optional[str] = Field(None, description="Role of the user.") + is_active: Optional[bool] = Field(None, description="Whether the user is active.") + uid: Optional[str] = Field(None, description="Unique identifier for the user.") + date_joined: Optional[str] = Field(None, description="Date the user joined the source organizaation.") + + +class AddMember(MemberBase, BaseModel): + source_uid: Optional[str] = Field(None, description="Unique identifier for the source.") + user_uid: Optional[str] = Field(None, description="Unique identifier for the user.") + role: Optional[str] = Field(None, description="Role of the user.") + is_active: Optional[bool] = Field(None, description="Whether the user is active.") + + +class MemberList(PaginatedResponse, BaseModel): + results: Optional[List[Member]] = None + +