From 3f1feb1f68704df974fa0758c568a3bfd8931566 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Mon, 13 Jan 2025 16:21:52 +0200 Subject: [PATCH 01/21] Port server changes --- cvat/apps/dataset_manager/bindings.py | 9 ++-- cvat/apps/dataset_manager/task.py | 1 + cvat/apps/engine/backup.py | 33 ++++++++------ ...consensus_jobs_per_regular_job_and_more.py | 44 +++++++++++++++++++ cvat/apps/engine/models.py | 4 ++ cvat/apps/engine/serializers.py | 21 ++++++--- cvat/apps/engine/task.py | 6 +++ cvat/apps/engine/views.py | 2 +- 8 files changed, 97 insertions(+), 23 deletions(-) create mode 100644 cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_jobs_per_regular_job_and_more.py diff --git a/cvat/apps/dataset_manager/bindings.py b/cvat/apps/dataset_manager/bindings.py index a2de70fce159..28b91f509e0f 100644 --- a/cvat/apps/dataset_manager/bindings.py +++ b/cvat/apps/dataset_manager/bindings.py @@ -780,6 +780,7 @@ def _init_meta(self): ("start_frame", str(self._db_data.start_frame + db_segment.start_frame * self._frame_step)), ("stop_frame", str(self._db_data.start_frame + db_segment.stop_frame * self._frame_step)), ("frame_filter", self._db_data.frame_filter), + ("parent_job_id", str(self._db_job.parent_job_id)), ("segments", [ ("segment", OrderedDict([ ("id", str(db_segment.id)), @@ -2178,6 +2179,8 @@ def import_dm_annotations(dm_dataset: dm.Dataset, instance_data: Union[ProjectDa dm.AnnotationType.mask: ShapeType.MASK } + sources = {'auto', 'semi-auto', 'manual', 'file', 'consensus'} + track_formats = [ 'cvat', 'datumaro', @@ -2258,7 +2261,7 @@ def import_dm_annotations(dm_dataset: dm.Dataset, instance_data: Union[ProjectDa track_id = ann.attributes.pop('track_id', None) source = ann.attributes.pop('source').lower() \ - if ann.attributes.get('source', '').lower() in {'auto', 'semi-auto', 'manual', 'file'} else 'manual' + if ann.attributes.get('source', '').lower() in sources else 'manual' shape_type = shapes[ann.type] if track_id is None or 'keyframe' not in ann.attributes or dm_dataset.format not in track_formats: @@ -2272,7 +2275,7 @@ def import_dm_annotations(dm_dataset: dm.Dataset, instance_data: Union[ProjectDa element_occluded = element.visibility[0] == dm.Points.Visibility.hidden element_outside = element.visibility[0] == dm.Points.Visibility.absent element_source = element.attributes.pop('source').lower() \ - if element.attributes.get('source', '').lower() in {'auto', 'semi-auto', 'manual', 'file'} else 'manual' + if element.attributes.get('source', '').lower() in sources else 'manual' elements.append(instance_data.LabeledShape( type=shapes[element.type], frame=frame_number, @@ -2344,7 +2347,7 @@ def import_dm_annotations(dm_dataset: dm.Dataset, instance_data: Union[ProjectDa for n, v in element.attributes.items() ] element_source = element.attributes.pop('source').lower() \ - if element.attributes.get('source', '').lower() in {'auto', 'semi-auto', 'manual', 'file'} else 'manual' + if element.attributes.get('source', '').lower() in sources else 'manual' tracks[track_id]['elements'][element.label].shapes.append(instance_data.TrackedShape( type=shapes[element.type], diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index 74f035d40787..ab0936caefb1 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -814,6 +814,7 @@ def __init__(self, pk): Prefetch('data__images', queryset=models.Image.objects.order_by('frame')) ).get(id=pk) + # TODO: maybe include consensus jobs except for task export requested_job_types = [models.JobType.ANNOTATION] if self.db_task.data.validation_mode == models.ValidationMode.GT_POOL: requested_job_types.append(models.JobType.GROUND_TRUTH) diff --git a/cvat/apps/engine/backup.py b/cvat/apps/engine/backup.py index f3790427f5ba..42baffc38c77 100644 --- a/cvat/apps/engine/backup.py +++ b/cvat/apps/engine/backup.py @@ -209,6 +209,7 @@ def _prepare_task_meta(self, task): 'status', 'subset', 'labels', + 'consensus_jobs_per_regular_job', } return self._prepare_meta(allowed_fields, task) @@ -324,7 +325,7 @@ def _get_db_jobs(self): if self._db_task: db_segments = list(self._db_task.segment_set.all().prefetch_related('job_set')) db_segments.sort(key=lambda i: i.job_set.first().id) - db_jobs = (s.job_set.first() for s in db_segments) + db_jobs = (job for s in db_segments for job in s.job_set.all()) return db_jobs return () @@ -437,17 +438,19 @@ def serialize_task(): return task def serialize_segment(db_segment): - db_job = db_segment.job_set.first() - job_serializer = SimpleJobSerializer(db_job) - for field in ('url', 'assignee'): - job_serializer.fields.pop(field) - job_data = self._prepare_job_meta(job_serializer.data) - - segment_serializer = SegmentSerializer(db_segment) - segment_serializer.fields.pop('jobs') - segment = segment_serializer.data - segment_type = segment.pop("type") - segment.update(job_data) + segments = [] + db_jobs = db_segment.job_set.all() + for db_job in db_jobs: + job_serializer = SimpleJobSerializer(db_job) + for field in ('url', 'assignee'): + job_serializer.fields.pop(field) + job_data = self._prepare_job_meta(job_serializer.data) + + segment_serializer = SegmentSerializer(db_segment) + segment_serializer.fields.pop('jobs') + segment = segment_serializer.data + segment_type = segment.pop("type") + segment.update(job_data) if ( self._db_task.segment_size == 0 and segment_type == models.SegmentType.RANGE @@ -455,12 +458,14 @@ def serialize_segment(db_segment): ): segment.update(serialize_segment_file_names(db_segment)) - return segment + segments.append(segment) + + return segments def serialize_jobs(): db_segments = list(self._db_task.segment_set.all()) db_segments.sort(key=lambda i: i.job_set.first().id) - return (serialize_segment(s) for s in db_segments) + return (serialized_job for s in db_segments for serialized_job in serialize_segment(s)) def serialize_segment_file_names(db_segment: models.Segment): if self._db_task.mode == 'annotation': diff --git a/cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_jobs_per_regular_job_and_more.py b/cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_jobs_per_regular_job_and_more.py new file mode 100644 index 000000000000..7819815db4b9 --- /dev/null +++ b/cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_jobs_per_regular_job_and_more.py @@ -0,0 +1,44 @@ +# Generated by Django 4.2.17 on 2025-01-13 14:21 + +import cvat.apps.engine.models +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("engine", "0086_profile_has_analytics_access"), + ] + + operations = [ + migrations.AddField( + model_name="job", + name="parent_job", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="children_jobs", + to="engine.job", + ), + ), + migrations.AddField( + model_name="task", + name="consensus_jobs_per_regular_job", + field=models.IntegerField(blank=True, default=0), + ), + migrations.AlterField( + model_name="job", + name="type", + field=models.CharField( + choices=[ + ("annotation", "ANNOTATION"), + ("ground_truth", "GROUND_TRUTH"), + ("consensus", "CONSENSUS"), + ], + default=cvat.apps.engine.models.JobType["ANNOTATION"], + max_length=32, + ), + ), + ] diff --git a/cvat/apps/engine/models.py b/cvat/apps/engine/models.py index c25c75404eaf..0e97a91dc145 100644 --- a/cvat/apps/engine/models.py +++ b/cvat/apps/engine/models.py @@ -168,6 +168,7 @@ def __str__(self): class JobType(str, Enum): ANNOTATION = 'annotation' GROUND_TRUTH = 'ground_truth' + CONSENSUS = 'consensus' @classmethod def choices(cls): @@ -531,6 +532,7 @@ def with_job_summary(self): return self.prefetch_related( 'segment_set__job_set', ).annotate( + total_jobs_count=models.Count('segment__job', distinct=True), completed_jobs_count=models.Count( 'segment__job', filter=models.Q(segment__job__state=StateChoice.COMPLETED.value) & @@ -573,6 +575,7 @@ class Task(TimestampedModel): blank=True, on_delete=models.SET_NULL, related_name='+') target_storage = models.ForeignKey('Storage', null=True, default=None, blank=True, on_delete=models.SET_NULL, related_name='+') + consensus_jobs_per_regular_job = models.IntegerField(default=0, blank=True) segment_set: models.manager.RelatedManager[Segment] @@ -859,6 +862,7 @@ class Job(TimestampedModel): type = models.CharField(max_length=32, choices=JobType.choices(), default=JobType.ANNOTATION) + parent_job = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children_jobs') def get_target_storage(self) -> Optional[Storage]: return self.segment.task.target_storage diff --git a/cvat/apps/engine/serializers.py b/cvat/apps/engine/serializers.py index 6c760b42ba65..85c618e491c5 100644 --- a/cvat/apps/engine/serializers.py +++ b/cvat/apps/engine/serializers.py @@ -158,13 +158,15 @@ def bind(self, field_name, parent): def get_fields(self): fields = super().get_fields() fields['url'] = HyperlinkedEndpointSerializer(self._model, filter_key=self._url_filter_key) - fields['count'].source = self._collection_key + '.count' + if not fields['count'].source: + fields['count'].source = self._collection_key + '.count' return fields def get_attribute(self, instance): return instance class JobsSummarySerializer(_CollectionSummarySerializer): + count = serializers.IntegerField(source='total_jobs_count', allow_null=True) completed = serializers.IntegerField(source='completed_jobs_count', allow_null=True) validation = serializers.IntegerField(source='validation_jobs_count', allow_null=True) @@ -635,6 +637,7 @@ class JobReadSerializer(serializers.ModelSerializer): issues = IssuesSummarySerializer(source='*') target_storage = StorageSerializer(required=False, allow_null=True) source_storage = StorageSerializer(required=False, allow_null=True) + parent_job_id = serializers.ReadOnlyField(allow_null=True) class Meta: model = models.Job @@ -643,7 +646,7 @@ class Meta: 'start_frame', 'stop_frame', 'data_chunk_size', 'data_compressed_chunk_type', 'data_original_chunk_type', 'created_date', 'updated_date', 'issues', 'labels', 'type', 'organization', - 'target_storage', 'source_storage', 'assignee_updated_date') + 'target_storage', 'source_storage', 'assignee_updated_date', 'parent_job_id') read_only_fields = fields def to_representation(self, instance): @@ -2222,6 +2225,7 @@ class TaskReadSerializer(serializers.ModelSerializer): source='data.validation_mode', required=False, allow_null=True, help_text="Describes how the task validation is performed. Configured at task creation" ) + consensus_jobs_per_regular_job = serializers.ReadOnlyField(required=False, allow_null=True) class Meta: model = models.Task @@ -2230,7 +2234,7 @@ class Meta: 'status', 'data_chunk_size', 'data_compressed_chunk_type', 'guide_id', 'data_original_chunk_type', 'size', 'image_quality', 'data', 'dimension', 'subset', 'organization', 'target_storage', 'source_storage', 'jobs', 'labels', - 'assignee_updated_date', 'validation_mode' + 'assignee_updated_date', 'validation_mode', 'consensus_jobs_per_regular_job', ) read_only_fields = fields extra_kwargs = { @@ -2246,12 +2250,14 @@ class TaskWriteSerializer(WriteOnceMixin, serializers.ModelSerializer): project_id = serializers.IntegerField(required=False, allow_null=True) target_storage = StorageSerializer(required=False, allow_null=True) source_storage = StorageSerializer(required=False, allow_null=True) + consensus_jobs_per_regular_job = serializers.IntegerField(required=False, allow_null=True) class Meta: model = models.Task - fields = ('url', 'id', 'name', 'project_id', 'owner_id', 'assignee_id', + fields = ( + 'url', 'id', 'name', 'project_id', 'owner_id', 'assignee_id', 'bug_tracker', 'overlap', 'segment_size', 'labels', 'subset', - 'target_storage', 'source_storage' + 'target_storage', 'source_storage', 'consensus_jobs_per_regular_job', ) write_once_fields = ('overlap', 'segment_size') @@ -2440,6 +2446,11 @@ def validate(self, attrs): if sublabels != target_project_sublabel_names.get(label): raise serializers.ValidationError('All task or project label names must be mapped to the target project') + consensus_jobs_per_regular_job = attrs.get('consensus_jobs_per_regular_job', self.instance.consensus_jobs_per_regular_job if self.instance else None) + + if consensus_jobs_per_regular_job and (consensus_jobs_per_regular_job == 1 or consensus_jobs_per_regular_job < 0 or consensus_jobs_per_regular_job > 10): + raise serializers.ValidationError("Consensus jobs per regular job shouldn't be negative, less than 10 except 1") + return attrs class ProjectReadSerializer(serializers.ModelSerializer): diff --git a/cvat/apps/engine/task.py b/cvat/apps/engine/task.py index 7aa92acba2fd..26532f3909f5 100644 --- a/cvat/apps/engine/task.py +++ b/cvat/apps/engine/task.py @@ -224,6 +224,12 @@ def _create_segments_and_jobs( db_job.save() db_job.make_dirs() + # consensus jobs use the same `db_segment` as the regular job, thus data not duplicated in backups, exports + for _ in range(db_task.consensus_jobs_per_regular_job): + consensus_db_job = models.Job(segment=db_segment, parent_job_id=db_job.id, type=models.JobType.CONSENSUS) + consensus_db_job.save() + consensus_db_job.make_dirs() + db_task.data.save() db_task.save() diff --git a/cvat/apps/engine/views.py b/cvat/apps/engine/views.py index 6b70836d53a1..fb2ce764c0e6 100644 --- a/cvat/apps/engine/views.py +++ b/cvat/apps/engine/views.py @@ -1949,7 +1949,7 @@ class JobViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateMo iam_organization_field = 'segment__task__organization' search_fields = ('task_name', 'project_name', 'assignee', 'state', 'stage') filter_fields = list(search_fields) + [ - 'id', 'task_id', 'project_id', 'updated_date', 'dimension', 'type' + 'id', 'task_id', 'project_id', 'updated_date', 'dimension', 'type', 'parent_job_id', ] simple_filters = list(set(filter_fields) - {'id', 'updated_date'}) ordering_fields = list(filter_fields) From 00993e54d516169542dae4daa05b7dbbf440e02f Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Mon, 13 Jan 2025 16:22:11 +0200 Subject: [PATCH 02/21] Port ui changes --- cvat-core/src/enums.ts | 1 + cvat-core/src/server-response-types.ts | 2 + cvat-core/src/session-implementation.ts | 4 + cvat-core/src/session.ts | 18 ++- cvat-ui/src/actions/jobs-actions.ts | 16 +++ cvat-ui/src/actions/tasks-actions.ts | 8 +- .../advanced-configuration-form.tsx | 58 +++++---- .../create-task-page/create-task-content.tsx | 1 + cvat-ui/src/components/job-item/job-item.tsx | 122 ++++++++++++------ cvat-ui/src/components/job-item/styles.scss | 14 ++ cvat-ui/src/components/jobs-page/job-card.tsx | 18 ++- .../jobs-page/jobs-filter-configuration.ts | 13 ++ cvat-ui/src/components/jobs-page/styles.scss | 18 +++ cvat-ui/src/components/task-page/details.tsx | 48 ++++--- cvat-ui/src/components/task-page/job-list.tsx | 3 + cvat-ui/src/reducers/index.ts | 3 + cvat-ui/src/reducers/jobs-reducer.ts | 23 ++++ cvat-ui/src/utils/validate-integer.ts | 37 ++++++ 18 files changed, 321 insertions(+), 86 deletions(-) create mode 100644 cvat-ui/src/utils/validate-integer.ts diff --git a/cvat-core/src/enums.ts b/cvat-core/src/enums.ts index 25fdf815fa20..abde94ff2ec9 100644 --- a/cvat-core/src/enums.ts +++ b/cvat-core/src/enums.ts @@ -37,6 +37,7 @@ export enum JobState { export enum JobType { ANNOTATION = 'annotation', GROUND_TRUTH = 'ground_truth', + CONSENSUS = 'consensus', } export enum DimensionType { diff --git a/cvat-core/src/server-response-types.ts b/cvat-core/src/server-response-types.ts index ef635d12004e..798b97bbbcdd 100644 --- a/cvat-core/src/server-response-types.ts +++ b/cvat-core/src/server-response-types.ts @@ -121,6 +121,7 @@ export interface SerializedTask { subset: string; updated_date: string; url: string; + consensus_jobs_per_regular_job: number; } export interface SerializedJob { @@ -147,6 +148,7 @@ export interface SerializedJob { url: string; source_storage: SerializedStorage | null; target_storage: SerializedStorage | null; + parent_job_id: number | null; } export type AttrInputType = 'select' | 'radio' | 'checkbox' | 'number' | 'text'; diff --git a/cvat-core/src/session-implementation.ts b/cvat-core/src/session-implementation.ts index a5c008605749..5435dcad2a86 100644 --- a/cvat-core/src/session-implementation.ts +++ b/cvat-core/src/session-implementation.ts @@ -748,6 +748,10 @@ export function implementTask(Task: typeof TaskClass): typeof TaskClass { taskSpec.source_storage = this.sourceStorage.toJSON(); } + if (this.consensusJobsPerRegularJob) { + taskSpec.consensus_jobs_per_regular_job = this.consensusJobsPerRegularJob; + } + const taskDataSpec = { client_files: this.clientFiles, server_files: this.serverFiles, diff --git a/cvat-core/src/session.ts b/cvat-core/src/session.ts index b3269ee78076..4189e1b326b7 100644 --- a/cvat-core/src/session.ts +++ b/cvat-core/src/session.ts @@ -494,7 +494,7 @@ export class Job extends Session { frame_count?: number; project_id: number | null; guide_id: number | null; - task_id: number | null; + task_id: number; labels: Label[]; dimension?: DimensionType; data_compressed_chunk_type?: ChunkType; @@ -505,8 +505,8 @@ export class Job extends Session { updated_date?: string, source_storage: Storage, target_storage: Storage, + parent_job_id: number | null; }; - constructor(initialData: InitializerType) { super(); @@ -532,6 +532,7 @@ export class Job extends Session { updated_date: undefined, source_storage: undefined, target_storage: undefined, + parent_job_id: null, }; this.#data.id = initialData.id ?? this.#data.id; @@ -546,6 +547,7 @@ export class Job extends Session { this.#data.data_chunk_size = initialData.data_chunk_size ?? this.#data.data_chunk_size; this.#data.mode = initialData.mode ?? this.#data.mode; this.#data.created_date = initialData.created_date ?? this.#data.created_date; + this.#data.parent_job_id = initialData.parent_job_id ?? this.#data.parent_job_id; if (Array.isArray(initialData.labels)) { this.#data.labels = initialData.labels.map((labelData) => { @@ -641,7 +643,7 @@ export class Job extends Session { return this.#data.guide_id; } - public get taskId(): number | null { + public get taskId(): number { return this.#data.task_id; } @@ -649,6 +651,10 @@ export class Job extends Session { return this.#data.dimension; } + public get parent_job_id(): number | null { + return this.#data.parent_job_id; + } + public get dataChunkType(): ChunkType { return this.#data.data_compressed_chunk_type; } @@ -751,6 +757,7 @@ export class Task extends Session { public readonly organization: number | null; public readonly progress: { count: number; completed: number }; public readonly jobs: Job[]; + public readonly consensusJobsPerRegularJob: number; public readonly startFrame: number; public readonly stopFrame: number; @@ -810,6 +817,7 @@ export class Task extends Session { cloud_storage_id: undefined, sorting_method: undefined, files: undefined, + consensus_jobs_per_regular_job: undefined, validation_mode: null, }; @@ -887,6 +895,7 @@ export class Task extends Session { data_chunk_size: data.data_chunk_size, target_storage: initialData.target_storage, source_storage: initialData.source_storage, + parent_job_id: job.parent_job_id, }); data.jobs.push(jobInstance); } @@ -994,6 +1003,9 @@ export class Task extends Session { copyData: { get: () => data.copy_data, }, + consensusJobsPerRegularJob: { + get: () => data.consensus_jobs_per_regular_job, + }, labels: { get: () => [...data.labels], set: (labels: Label[]) => { diff --git a/cvat-ui/src/actions/jobs-actions.ts b/cvat-ui/src/actions/jobs-actions.ts index e7d13e23b7f1..4cb57d980307 100644 --- a/cvat-ui/src/actions/jobs-actions.ts +++ b/cvat-ui/src/actions/jobs-actions.ts @@ -27,6 +27,8 @@ export enum JobsActionTypes { DELETE_JOB = 'DELETE_JOB', DELETE_JOB_SUCCESS = 'DELETE_JOB_SUCCESS', DELETE_JOB_FAILED = 'DELETE_JOB_FAILED', + COLLAPSE_REGULAR_JOB = 'COLLAPSE_REGULAR_JOB', + UNCOLLAPSE_REGULAR_JOB = 'UNCOLLAPSE_REGULAR_JOB', } interface JobsList extends Array { @@ -71,6 +73,12 @@ const jobsActions = { deleteJobFailed: (jobID: number, error: any) => ( createAction(JobsActionTypes.DELETE_JOB_FAILED, { jobID, error }) ), + collapseRegularJob: (jobID: number) => ( + createAction(JobsActionTypes.COLLAPSE_REGULAR_JOB, { jobID }) + ), + uncollapseRegularJob: (jobID: number) => ( + createAction(JobsActionTypes.UNCOLLAPSE_REGULAR_JOB, { jobID }) + ), }; export type JobsActions = ActionUnion; @@ -156,3 +164,11 @@ export const deleteJobAsync = (job: Job): ThunkAction => async (dispatch) => { dispatch(jobsActions.deleteJobSuccess(job.id)); }; + +export const collapseRegularJob = (jobID: number, uncollapse: boolean): ThunkAction => async (dispatch) => { + if (uncollapse) { + dispatch(jobsActions.collapseRegularJob(jobID)); + } else { + dispatch(jobsActions.uncollapseRegularJob(jobID)); + } +}; diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts index 644f5aa7b021..70f5ff8c3531 100644 --- a/cvat-ui/src/actions/tasks-actions.ts +++ b/cvat-ui/src/actions/tasks-actions.ts @@ -222,8 +222,9 @@ ThunkAction { use_zip_chunks: data.advanced.useZipChunks, use_cache: data.advanced.useCache, sorting_method: data.advanced.sortingMethod, - source_storage: new Storage(data.advanced.sourceStorage ?? { location: StorageLocation.LOCAL }).toJSON(), - target_storage: new Storage(data.advanced.targetStorage ?? { location: StorageLocation.LOCAL }).toJSON(), + source_storage: new Storage(data.advanced.sourceStorage || { location: StorageLocation.LOCAL }).toJSON(), + target_storage: new Storage(data.advanced.targetStorage || { location: StorageLocation.LOCAL }).toJSON(), + consensus_jobs_per_regular_job: data.advanced.consensusJobsPerRegularJob, }; if (data.projectId) { @@ -275,6 +276,9 @@ ThunkAction { }, }; } + if (data.advanced.consensusJobsPerRegularJob) { + description.consensus_jobs_per_regular_job = +data.advanced.consensusJobsPerRegularJob; + } const taskInstance = new cvat.classes.Task(description); taskInstance.clientFiles = data.files.local; diff --git a/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx b/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx index 40dd1dca08af..508db77ce433 100644 --- a/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx +++ b/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx @@ -17,6 +17,7 @@ import Text from 'antd/lib/typography/Text'; import { Store } from 'antd/lib/form/interface'; import CVATTooltip from 'components/common/cvat-tooltip'; import patterns from 'utils/validation-patterns'; +import { isInteger } from 'utils/validate-integer'; import { StorageLocation } from 'reducers'; import SourceStorageField from 'components/storage/source-storage-field'; import TargetStorageField from 'components/storage/target-storage-field'; @@ -47,6 +48,7 @@ export interface AdvancedConfiguration { sortingMethod: SortingMethod; useProjectSourceStorage: boolean; useProjectTargetStorage: boolean; + consensusJobsPerRegularJob: number; sourceStorage: StorageData; targetStorage: StorageData; } @@ -59,6 +61,7 @@ const initialValues: AdvancedConfiguration = { sortingMethod: SortingMethod.LEXICOGRAPHICAL, useProjectSourceStorage: true, useProjectTargetStorage: true, + consensusJobsPerRegularJob: 0, sourceStorage: { location: StorageLocation.LOCAL, @@ -93,30 +96,6 @@ function validateURL(_: RuleObject, value: string): Promise { return Promise.resolve(); } -const isInteger = ({ min, max }: { min?: number; max?: number }) => ( - _: RuleObject, - value?: number | string, -): Promise => { - if (typeof value === 'undefined' || value === '') { - return Promise.resolve(); - } - - const intValue = +value; - if (Number.isNaN(intValue) || !Number.isInteger(intValue)) { - return Promise.reject(new Error('Value must be a positive integer')); - } - - if (typeof min !== 'undefined' && intValue < min) { - return Promise.reject(new Error(`Value must be more than ${min}`)); - } - - if (typeof max !== 'undefined' && intValue > max) { - return Promise.reject(new Error(`Value must be less than ${max}`)); - } - - return Promise.resolve(); -}; - const validateOverlapSize: RuleRender = ({ getFieldValue }): RuleObject => ({ validator(_: RuleObject, value?: string | number): Promise { if (typeof value !== 'undefined' && value !== '') { @@ -405,6 +384,32 @@ class AdvancedConfigurationForm extends React.PureComponent { ); } + private renderconsensusJobsPerRegularJob(): JSX.Element { + return ( + intValue !== 1, + }), + }, + ]} + > + + + ); + } + private renderSourceStorage(): JSX.Element { const { projectId, @@ -486,6 +491,11 @@ class AdvancedConfigurationForm extends React.PureComponent { {this.renderChunkSize()} + + + {this.renderconsensusJobsPerRegularJob()} + + {this.renderBugTracker()} diff --git a/cvat-ui/src/components/create-task-page/create-task-content.tsx b/cvat-ui/src/components/create-task-page/create-task-content.tsx index b1e4dc69008f..81925f672313 100644 --- a/cvat-ui/src/components/create-task-page/create-task-content.tsx +++ b/cvat-ui/src/components/create-task-page/create-task-content.tsx @@ -85,6 +85,7 @@ const defaultState: State = { }, useProjectSourceStorage: true, useProjectTargetStorage: true, + consensusJobsPerRegularJob: 0, }, quality: { validationMode: ValidationMode.NONE, diff --git a/cvat-ui/src/components/job-item/job-item.tsx b/cvat-ui/src/components/job-item/job-item.tsx index 3fe5267f9ef2..54e11f18a645 100644 --- a/cvat-ui/src/components/job-item/job-item.tsx +++ b/cvat-ui/src/components/job-item/job-item.tsx @@ -5,6 +5,7 @@ import './styles.scss'; import React, { useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { Link } from 'react-router-dom'; import moment from 'moment'; import { Col, Row } from 'antd/lib/grid'; @@ -25,8 +26,9 @@ import { import { useIsMounted } from 'utils/hooks'; import UserSelector from 'components/task-page/user-selector'; import CVATTooltip from 'components/common/cvat-tooltip'; -import { useSelector } from 'react-redux'; import { CombinedState } from 'reducers'; +import Collapse from 'antd/lib/collapse'; +import { collapseRegularJob } from 'actions/jobs-actions'; import JobActionsMenu from './job-actions-menu'; interface Props { @@ -105,6 +107,7 @@ function JobItem(props: Props): JSX.Element { const deleted = job.id in deletes ? deletes[job.id] === true : false; const { stage } = job; + const dispatch = useDispatch(); const created = moment(job.createdDate); const updated = moment(job.updatedDate); const now = moment(moment.now()); @@ -116,6 +119,40 @@ function JobItem(props: Props): JSX.Element { } const frameCountPercent = ((job.frameCount / (task.size || 1)) * 100).toFixed(0); const frameCountPercentRepresentation = frameCountPercent === '0' ? '<1' : frameCountPercent; + let jobName = `Job #${job.id}`; + if (task.consensusJobsPerRegularJob && job.type !== JobType.GROUND_TRUTH) { + jobName = `Job #${job.id}`; + } + + let consensusJobs: Job[] = []; + if (task.consensusJobsPerRegularJob) { + consensusJobs = task.jobs.filter((eachJob: Job) => eachJob.parent_job_id === job.id).reverse(); + } + const consensusJobViews: React.JSX.Element[] = consensusJobs.map((eachJob: Job) => ( + + )); + + const regularJobViewUncollapse = useSelector((state: CombinedState) => state.jobs.regularJobViewUncollapse); + const regularJobUncollapsed = regularJobViewUncollapse[job.id]; + const handleCollapseChange = async (): Promise => { + await dispatch(collapseRegularJob(job.id, !regularJobUncollapsed)); + }; + + let tag = null; + if (job.type === JobType.GROUND_TRUTH) { + tag = ( + + Ground truth + + ); + } else if (job.type === JobType.CONSENSUS) { + tag = ( + + Consensus + + ); + } + return ( @@ -123,21 +160,16 @@ function JobItem(props: Props): JSX.Element { - {`Job #${job.id}`} + {jobName} - { - job.type === JobType.GROUND_TRUTH ? ( - - Ground truth - - ) : ( - - }> - - - - ) - } + {tag} + {job.type !== JobType.GROUND_TRUTH && ( + + }> + + + + )} @@ -208,15 +240,11 @@ function JobItem(props: Props): JSX.Element { onJobUpdate(job, { state: newValue }); }} > - - {JobState.NEW} - + {JobState.NEW} {JobState.IN_PROGRESS} - - {JobState.REJECTED} - + {JobState.REJECTED} {JobState.COMPLETED} @@ -233,7 +261,11 @@ function JobItem(props: Props): JSX.Element { Duration: - {`${moment.duration(now.diff(created)).humanize()}`} + + {`${moment + .duration(now.diff(created)) + .humanize()}`} + @@ -245,19 +277,17 @@ function JobItem(props: Props): JSX.Element { - { - job.type !== JobType.GROUND_TRUTH && ( - - - - Frame range: - - {`${job.startFrame}-${job.stopFrame}`} - - - - ) - } + {job.type !== JobType.GROUND_TRUTH && ( + + + + Frame range: + + {`${job.startFrame}-${job.stopFrame}`} + + + + )} @@ -265,10 +295,30 @@ function JobItem(props: Props): JSX.Element { } + className='job-actions-menu' + overlay={( + + )} > + {consensusJobs.length > 0 && ( + {`${consensusJobs.length} Consensus Jobs`}, + children: consensusJobViews, + }, + ]} + /> + )} ); diff --git a/cvat-ui/src/components/job-item/styles.scss b/cvat-ui/src/components/job-item/styles.scss index 39a137000ea7..7723a24dec1a 100644 --- a/cvat-ui/src/components/job-item/styles.scss +++ b/cvat-ui/src/components/job-item/styles.scss @@ -71,6 +71,20 @@ .cvat-job-item-dates-info { margin-top: $grid-unit-size; } + + .cvat-consensus-job-collapse { + margin-top: 12px; + + .ant-collapse-item > .ant-collapse-header { + align-items: center; + } + } + + .job-actions-menu { + position: absolute; + top: $grid-unit-size * 6.5; + } + } .ant-menu.cvat-job-item-menu { diff --git a/cvat-ui/src/components/jobs-page/job-card.tsx b/cvat-ui/src/components/jobs-page/job-card.tsx index a76ed0c38146..6d2593c44ba6 100644 --- a/cvat-ui/src/components/jobs-page/job-card.tsx +++ b/cvat-ui/src/components/jobs-page/job-card.tsx @@ -11,7 +11,7 @@ import Descriptions from 'antd/lib/descriptions'; import { MoreOutlined } from '@ant-design/icons'; import Dropdown from 'antd/lib/dropdown'; -import { Job } from 'cvat-core-wrapper'; +import { Job, JobType } from 'cvat-core-wrapper'; import { useCardHeightHOC } from 'utils/hooks'; import Preview from 'components/common/preview'; import JobActionsMenu from 'components/job-item/job-actions-menu'; @@ -52,6 +52,13 @@ function JobCardComponent(props: Props): JSX.Element { (style as any).opacity = 0.5; } + let tag = null; + if (job.type === JobType.GROUND_TRUTH) { + tag = 'Ground truth'; + } else if (job.type === JobType.CONSENSUS) { + tag = 'Consensus'; + } + return ( + {tag &&
{tag}
}
{job.dimension.toUpperCase()}
)} @@ -78,14 +86,16 @@ function JobCardComponent(props: Props): JSX.Element { {`${job.stage} ${job.state}`} {job.stopFrame - job.startFrame + 1} - { job.assignee ? ( + {job.assignee ? ( {job.assignee.username} - ) : } + ) : ( + + )} )} + overlay={} > diff --git a/cvat-ui/src/components/jobs-page/jobs-filter-configuration.ts b/cvat-ui/src/components/jobs-page/jobs-filter-configuration.ts index 8c48a8f8609c..ea95e4fe3362 100644 --- a/cvat-ui/src/components/jobs-page/jobs-filter-configuration.ts +++ b/cvat-ui/src/components/jobs-page/jobs-filter-configuration.ts @@ -96,6 +96,19 @@ export const config: Partial = { valueSources: ['value'], operators: ['like'], }, + type: { + label: 'Job Type', + type: 'select', + operators: ['select_equals'], + valueSources: ['value'], + fieldSettings: { + listValues: [ + { value: 'annotation', title: 'annotation' }, + { value: 'ground_truth', title: 'ground_truth' }, + { value: 'consensus', title: 'consensus' }, + ], + }, + }, }, }; diff --git a/cvat-ui/src/components/jobs-page/styles.scss b/cvat-ui/src/components/jobs-page/styles.scss index e3a011a720a8..40481b9aa382 100644 --- a/cvat-ui/src/components/jobs-page/styles.scss +++ b/cvat-ui/src/components/jobs-page/styles.scss @@ -75,6 +75,10 @@ .cvat-job-page-list-item-dimension { opacity: 1; } + + .cvat-job-page-list-item-type { + opacity: 1; + } } :nth-child(4n) { @@ -135,6 +139,20 @@ transition: 0.15s all ease; box-shadow: $box-shadow-base; } + + .cvat-job-page-list-item-type { + position: absolute; + top: $grid-unit-size * 5; + left: 0; + margin: $grid-unit-size $grid-unit-size $grid-unit-size 0; + width: fit-content; + background: white; + border-radius: 0 $border-radius-base $border-radius-base 0; + padding: $grid-unit-size; + opacity: 0.5; + transition: 0.15s all ease; + box-shadow: $box-shadow-base; + } } .cvat-jobs-page-pagination { diff --git a/cvat-ui/src/components/task-page/details.tsx b/cvat-ui/src/components/task-page/details.tsx index c1c986260bfb..1d2a6c9f4c5b 100644 --- a/cvat-ui/src/components/task-page/details.tsx +++ b/cvat-ui/src/components/task-page/details.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { connect } from 'react-redux'; import { Row, Col } from 'antd/lib/grid'; +import Tag from 'antd/lib/tag'; import Text from 'antd/lib/typography/Text'; import Title from 'antd/lib/typography/Title'; import moment from 'moment'; @@ -59,6 +60,7 @@ const core = getCore(); interface State { name: string; subset: string; + consensusJobsPerRegularJob: number; } type Props = DispatchToProps & StateToProps & OwnProps; @@ -70,6 +72,7 @@ class DetailsComponent extends React.PureComponent { this.state = { name: taskInstance.name, subset: taskInstance.subset, + consensusJobsPerRegularJob: taskInstance.consensusJobsPerRegularJob, }; } @@ -86,29 +89,35 @@ class DetailsComponent extends React.PureComponent { private renderTaskName(): JSX.Element { const { name } = this.state; const { task: taskInstance, onUpdateTask } = this.props; + const taskName = name; return ( - { - this.setState({ - name: value, - }); - - taskInstance.name = value; - onUpdateTask(taskInstance); - }, - }} - className='cvat-text-color cvat-task-name' - > - {name} - + + + { + this.setState({ + name: value, + }); + + taskInstance.name = value; + onUpdateTask(taskInstance); + }, + }} + className='cvat-text-color cvat-task-name' + > + {taskName} + + + ); } private renderDescription(): JSX.Element { const { task: taskInstance, onUpdateTask } = this.props; + const { consensusJobsPerRegularJob } = this.state; const owner = taskInstance.owner ? taskInstance.owner.username : null; const assignee = taskInstance.assignee ? taskInstance.assignee : null; const created = moment(taskInstance.createdDate).format('MMMM Do YYYY'); @@ -127,8 +136,13 @@ class DetailsComponent extends React.PureComponent { {owner && ( - {`Task #${taskInstance.id} Created by ${owner} on ${created}`} +
+ + {`Task #${taskInstance.id} Created by ${owner} on ${created}`} + +
)} + {consensusJobsPerRegularJob > 0 && Consensus Based Annotation} Assigned to diff --git a/cvat-ui/src/components/task-page/job-list.tsx b/cvat-ui/src/components/task-page/job-list.tsx index bbd59652da2f..c6f52cc662d9 100644 --- a/cvat-ui/src/components/task-page/job-list.tsx +++ b/cvat-ui/src/components/task-page/job-list.tsx @@ -59,6 +59,9 @@ function setUpJobsList(jobs: Job[], query: JobsQuery): Job[] { result = result.filter((job, index) => jsonLogic.apply(filter, converted[index])); } + // consensus jobs will be under the collapse view + result = result.filter((job) => job.parent_job_id === null); + return result; } diff --git a/cvat-ui/src/reducers/index.ts b/cvat-ui/src/reducers/index.ts index 337ef29927b2..96cacb3003fa 100644 --- a/cvat-ui/src/reducers/index.ts +++ b/cvat-ui/src/reducers/index.ts @@ -89,6 +89,9 @@ export interface JobsState { [tid: number]: boolean; }; }; + regularJobViewUncollapse: { + [tid: number]: boolean; + }; } export interface TasksState { diff --git a/cvat-ui/src/reducers/jobs-reducer.ts b/cvat-ui/src/reducers/jobs-reducer.ts index 4be7c5c285f8..a448c51765ab 100644 --- a/cvat-ui/src/reducers/jobs-reducer.ts +++ b/cvat-ui/src/reducers/jobs-reducer.ts @@ -21,6 +21,7 @@ const defaultState: JobsState = { activities: { deletes: {}, }, + regularJobViewUncollapse: {}, }; export default (state: JobsState = defaultState, action: JobsActions): JobsState => { @@ -163,6 +164,28 @@ export default (state: JobsState = defaultState, action: JobsActions): JobsState fetching: false, }; } + case JobsActionTypes.COLLAPSE_REGULAR_JOB: { + const { jobID } = action.payload; + state.regularJobViewUncollapse[jobID] = true; + + return { + ...state, + regularJobViewUncollapse: { + ...state.regularJobViewUncollapse, + }, + }; + } + case JobsActionTypes.UNCOLLAPSE_REGULAR_JOB: { + const { jobID } = action.payload; + state.regularJobViewUncollapse[jobID] = false; + + return { + ...state, + regularJobViewUncollapse: { + ...state.regularJobViewUncollapse, + }, + }; + } default: { return state; } diff --git a/cvat-ui/src/utils/validate-integer.ts b/cvat-ui/src/utils/validate-integer.ts new file mode 100644 index 000000000000..9c6178757c06 --- /dev/null +++ b/cvat-ui/src/utils/validate-integer.ts @@ -0,0 +1,37 @@ +// Copyright (C) 2024 CVAT.ai Corporation +// +// SPDX-License-Identifier: MIT + +import { RuleObject } from 'antd/lib/form'; + +export const isInteger = ({ min, max, filter }: { + min?: number; + max?: number; + filter?: (intValue: number) => boolean; +}) => ( + _: RuleObject, + value?: number | string, +): Promise => { + if (typeof value === 'undefined' || value === '') { + return Promise.resolve(); + } + + const intValue = +value; + if (Number.isNaN(intValue) || !Number.isInteger(intValue)) { + return Promise.reject(new Error('Value must be a positive integer')); + } + + if (typeof min !== 'undefined' && intValue < min) { + return Promise.reject(new Error(`Value must be more than ${min}`)); + } + + if (typeof max !== 'undefined' && intValue > max) { + return Promise.reject(new Error(`Value must be less than ${max}`)); + } + + if (filter && !filter(intValue)) { + return Promise.reject(new Error(`Value can not be equal to ${intValue}`)); + } + + return Promise.resolve(); +}; From 389611f3ecbc033290e067fd4ba4d6d602f76889 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Mon, 13 Jan 2025 16:44:48 +0200 Subject: [PATCH 03/21] Rename task Consensus tag --- cvat-ui/src/components/task-page/details.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat-ui/src/components/task-page/details.tsx b/cvat-ui/src/components/task-page/details.tsx index 1d2a6c9f4c5b..e3db27f8e5dd 100644 --- a/cvat-ui/src/components/task-page/details.tsx +++ b/cvat-ui/src/components/task-page/details.tsx @@ -142,7 +142,7 @@ class DetailsComponent extends React.PureComponent { )} - {consensusJobsPerRegularJob > 0 && Consensus Based Annotation} + {consensusJobsPerRegularJob > 0 && Consensus} Assigned to From 7d05dfd821cf46c7fcd3f2ea44ed29a1693edd5b Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Mon, 13 Jan 2025 16:50:18 +0200 Subject: [PATCH 04/21] Move consensus tag to parent jobs --- cvat-ui/src/components/job-item/job-item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat-ui/src/components/job-item/job-item.tsx b/cvat-ui/src/components/job-item/job-item.tsx index 54e11f18a645..6656769c96c6 100644 --- a/cvat-ui/src/components/job-item/job-item.tsx +++ b/cvat-ui/src/components/job-item/job-item.tsx @@ -145,7 +145,7 @@ function JobItem(props: Props): JSX.Element { Ground truth ); - } else if (job.type === JobType.CONSENSUS) { + } else if (job.type === JobType.ANNOTATION && task.consensusJobsPerRegularJob) { tag = ( Consensus From 11389929b8ebee955e9c95a720bd6e912e79f612 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Mon, 13 Jan 2025 17:13:45 +0200 Subject: [PATCH 05/21] Fix job item column width --- cvat-ui/src/components/job-item/job-item.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/cvat-ui/src/components/job-item/job-item.tsx b/cvat-ui/src/components/job-item/job-item.tsx index 6656769c96c6..2f31eb133d98 100644 --- a/cvat-ui/src/components/job-item/job-item.tsx +++ b/cvat-ui/src/components/job-item/job-item.tsx @@ -7,7 +7,7 @@ import './styles.scss'; import React, { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Link } from 'react-router-dom'; -import moment from 'moment'; +import moment, { Moment } from 'moment'; import { Col, Row } from 'antd/lib/grid'; import Card from 'antd/lib/card'; import Text from 'antd/lib/typography/Text'; @@ -31,6 +31,10 @@ import Collapse from 'antd/lib/collapse'; import { collapseRegularJob } from 'actions/jobs-actions'; import JobActionsMenu from './job-actions-menu'; +function formatDate(value: Moment): string { + return value.format('MMM Do YYYY HH:mm'); +} + interface Props { job: Job; task: Task; @@ -157,7 +161,7 @@ function JobItem(props: Props): JSX.Element { - + {jobName} @@ -173,18 +177,18 @@ function JobItem(props: Props): JSX.Element { - Created on - {`${created.format('MMMM Do YYYY HH:mm')}`} + Created: + {`${formatDate(created)}`} - Last updated - {`${updated.format('MMMM Do YYYY HH:mm')}`} + Updated: + {`${formatDate(updated)}`} - + From 2dc777c264abbe986dad1d9a250add933d28b26f Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Mon, 13 Jan 2025 19:17:52 +0200 Subject: [PATCH 06/21] Rename consensus jobs per job to consensus replicas field --- cvat-core/src/server-response-types.ts | 2 +- cvat-core/src/session-implementation.ts | 4 +- cvat-core/src/session.ts | 8 ++-- cvat-ui/src/actions/tasks-actions.ts | 6 +-- .../advanced-configuration-form.tsx | 12 ++--- .../create-task-page/create-task-content.tsx | 2 +- cvat-ui/src/components/job-item/job-item.tsx | 8 ++-- cvat-ui/src/components/task-page/details.tsx | 8 ++-- cvat/apps/engine/backup.py | 6 +-- cvat/apps/engine/default_settings.py | 4 ++ ...t_job_task_consensus_replicas_and_more.py} | 6 +-- cvat/apps/engine/models.py | 3 +- cvat/apps/engine/serializers.py | 44 ++++++++++++++----- cvat/apps/engine/task.py | 2 +- cvat/schema.yml | 35 ++++++++++++++- 15 files changed, 103 insertions(+), 47 deletions(-) rename cvat/apps/engine/migrations/{0087_job_parent_job_task_consensus_jobs_per_regular_job_and_more.py => 0087_job_parent_job_task_consensus_replicas_and_more.py} (87%) diff --git a/cvat-core/src/server-response-types.ts b/cvat-core/src/server-response-types.ts index 798b97bbbcdd..e5921e3b7ef6 100644 --- a/cvat-core/src/server-response-types.ts +++ b/cvat-core/src/server-response-types.ts @@ -121,7 +121,7 @@ export interface SerializedTask { subset: string; updated_date: string; url: string; - consensus_jobs_per_regular_job: number; + consensus_replicas: number; } export interface SerializedJob { diff --git a/cvat-core/src/session-implementation.ts b/cvat-core/src/session-implementation.ts index 5435dcad2a86..b5f463c32e21 100644 --- a/cvat-core/src/session-implementation.ts +++ b/cvat-core/src/session-implementation.ts @@ -748,8 +748,8 @@ export function implementTask(Task: typeof TaskClass): typeof TaskClass { taskSpec.source_storage = this.sourceStorage.toJSON(); } - if (this.consensusJobsPerRegularJob) { - taskSpec.consensus_jobs_per_regular_job = this.consensusJobsPerRegularJob; + if (this.consensusReplicas) { + taskSpec.consensus_replicas = this.consensusReplicas; } const taskDataSpec = { diff --git a/cvat-core/src/session.ts b/cvat-core/src/session.ts index 4189e1b326b7..ff9475056f2e 100644 --- a/cvat-core/src/session.ts +++ b/cvat-core/src/session.ts @@ -757,7 +757,7 @@ export class Task extends Session { public readonly organization: number | null; public readonly progress: { count: number; completed: number }; public readonly jobs: Job[]; - public readonly consensusJobsPerRegularJob: number; + public readonly consensusReplicas: number; public readonly startFrame: number; public readonly stopFrame: number; @@ -817,7 +817,7 @@ export class Task extends Session { cloud_storage_id: undefined, sorting_method: undefined, files: undefined, - consensus_jobs_per_regular_job: undefined, + consensus_replicas: undefined, validation_mode: null, }; @@ -1003,8 +1003,8 @@ export class Task extends Session { copyData: { get: () => data.copy_data, }, - consensusJobsPerRegularJob: { - get: () => data.consensus_jobs_per_regular_job, + consensusReplicas: { + get: () => data.consensus_replicas, }, labels: { get: () => [...data.labels], diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts index 70f5ff8c3531..faad9e67f0a0 100644 --- a/cvat-ui/src/actions/tasks-actions.ts +++ b/cvat-ui/src/actions/tasks-actions.ts @@ -224,7 +224,7 @@ ThunkAction { sorting_method: data.advanced.sortingMethod, source_storage: new Storage(data.advanced.sourceStorage || { location: StorageLocation.LOCAL }).toJSON(), target_storage: new Storage(data.advanced.targetStorage || { location: StorageLocation.LOCAL }).toJSON(), - consensus_jobs_per_regular_job: data.advanced.consensusJobsPerRegularJob, + consensus_replicas: data.advanced.consensusReplicas, }; if (data.projectId) { @@ -276,8 +276,8 @@ ThunkAction { }, }; } - if (data.advanced.consensusJobsPerRegularJob) { - description.consensus_jobs_per_regular_job = +data.advanced.consensusJobsPerRegularJob; + if (data.advanced.consensusReplicas) { + description.consensus_replicas = +data.advanced.consensusReplicas; } const taskInstance = new cvat.classes.Task(description); diff --git a/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx b/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx index 508db77ce433..44661c8fd0ee 100644 --- a/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx +++ b/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx @@ -48,7 +48,7 @@ export interface AdvancedConfiguration { sortingMethod: SortingMethod; useProjectSourceStorage: boolean; useProjectTargetStorage: boolean; - consensusJobsPerRegularJob: number; + consensusReplicas: number; sourceStorage: StorageData; targetStorage: StorageData; } @@ -61,7 +61,7 @@ const initialValues: AdvancedConfiguration = { sortingMethod: SortingMethod.LEXICOGRAPHICAL, useProjectSourceStorage: true, useProjectTargetStorage: true, - consensusJobsPerRegularJob: 0, + consensusReplicas: 0, sourceStorage: { location: StorageLocation.LOCAL, @@ -384,11 +384,11 @@ class AdvancedConfigurationForm extends React.PureComponent { ); } - private renderconsensusJobsPerRegularJob(): JSX.Element { + private renderConsensusReplicas(): JSX.Element { return ( { - {this.renderconsensusJobsPerRegularJob()} + {this.renderConsensusReplicas()} diff --git a/cvat-ui/src/components/create-task-page/create-task-content.tsx b/cvat-ui/src/components/create-task-page/create-task-content.tsx index 81925f672313..5848a2a398b2 100644 --- a/cvat-ui/src/components/create-task-page/create-task-content.tsx +++ b/cvat-ui/src/components/create-task-page/create-task-content.tsx @@ -85,7 +85,7 @@ const defaultState: State = { }, useProjectSourceStorage: true, useProjectTargetStorage: true, - consensusJobsPerRegularJob: 0, + consensusReplicas: 0, }, quality: { validationMode: ValidationMode.NONE, diff --git a/cvat-ui/src/components/job-item/job-item.tsx b/cvat-ui/src/components/job-item/job-item.tsx index 2f31eb133d98..9565f6e0c9f7 100644 --- a/cvat-ui/src/components/job-item/job-item.tsx +++ b/cvat-ui/src/components/job-item/job-item.tsx @@ -124,12 +124,12 @@ function JobItem(props: Props): JSX.Element { const frameCountPercent = ((job.frameCount / (task.size || 1)) * 100).toFixed(0); const frameCountPercentRepresentation = frameCountPercent === '0' ? '<1' : frameCountPercent; let jobName = `Job #${job.id}`; - if (task.consensusJobsPerRegularJob && job.type !== JobType.GROUND_TRUTH) { + if (task.consensusReplicas && job.type !== JobType.GROUND_TRUTH) { jobName = `Job #${job.id}`; } let consensusJobs: Job[] = []; - if (task.consensusJobsPerRegularJob) { + if (task.consensusReplicas) { consensusJobs = task.jobs.filter((eachJob: Job) => eachJob.parent_job_id === job.id).reverse(); } const consensusJobViews: React.JSX.Element[] = consensusJobs.map((eachJob: Job) => ( @@ -149,7 +149,7 @@ function JobItem(props: Props): JSX.Element { Ground truth ); - } else if (job.type === JobType.ANNOTATION && task.consensusJobsPerRegularJob) { + } else if (job.type === JobType.ANNOTATION && task.consensusReplicas) { tag = ( Consensus @@ -303,7 +303,7 @@ function JobItem(props: Props): JSX.Element { overlay={( )} > diff --git a/cvat-ui/src/components/task-page/details.tsx b/cvat-ui/src/components/task-page/details.tsx index e3db27f8e5dd..73c62e7e8f01 100644 --- a/cvat-ui/src/components/task-page/details.tsx +++ b/cvat-ui/src/components/task-page/details.tsx @@ -60,7 +60,7 @@ const core = getCore(); interface State { name: string; subset: string; - consensusJobsPerRegularJob: number; + consensusReplicas: number; } type Props = DispatchToProps & StateToProps & OwnProps; @@ -72,7 +72,7 @@ class DetailsComponent extends React.PureComponent { this.state = { name: taskInstance.name, subset: taskInstance.subset, - consensusJobsPerRegularJob: taskInstance.consensusJobsPerRegularJob, + consensusReplicas: taskInstance.consensusReplicas, }; } @@ -117,7 +117,7 @@ class DetailsComponent extends React.PureComponent { private renderDescription(): JSX.Element { const { task: taskInstance, onUpdateTask } = this.props; - const { consensusJobsPerRegularJob } = this.state; + const { consensusReplicas } = this.state; const owner = taskInstance.owner ? taskInstance.owner.username : null; const assignee = taskInstance.assignee ? taskInstance.assignee : null; const created = moment(taskInstance.createdDate).format('MMMM Do YYYY'); @@ -142,7 +142,7 @@ class DetailsComponent extends React.PureComponent { )} - {consensusJobsPerRegularJob > 0 && Consensus} + {consensusReplicas > 0 && Consensus} Assigned to diff --git a/cvat/apps/engine/backup.py b/cvat/apps/engine/backup.py index 42baffc38c77..6b442be89fef 100644 --- a/cvat/apps/engine/backup.py +++ b/cvat/apps/engine/backup.py @@ -209,7 +209,7 @@ def _prepare_task_meta(self, task): 'status', 'subset', 'labels', - 'consensus_jobs_per_regular_job', + 'consensus_replicas', } return self._prepare_meta(allowed_fields, task) @@ -878,7 +878,7 @@ def _import_gt_jobs(self, jobs): }) job_serializer.is_valid(raise_exception=True) job_serializer.save() - elif job_type == models.JobType.ANNOTATION: + elif job_type in [models.JobType.ANNOTATION, models.JobType.CONSENSUS]: continue else: assert False @@ -913,7 +913,7 @@ class _ProjectBackupBase(_BackupBase): def _prepare_project_meta(self, project): allowed_fields = { 'bug_tracker', - 'deimension', + 'dimension', 'labels', 'name', 'status', diff --git a/cvat/apps/engine/default_settings.py b/cvat/apps/engine/default_settings.py index f853d3bc8219..72469b478daa 100644 --- a/cvat/apps/engine/default_settings.py +++ b/cvat/apps/engine/default_settings.py @@ -91,3 +91,7 @@ EXPORT_LOCKED_RETRY_INTERVAL = int( os.getenv("CVAT_EXPORT_LOCKED_RETRY_INTERVAL", default_export_locked_retry_interval) ) + +MAX_CONSENSUS_REPLICAS = int(os.getenv("CVAT_MAX_CONSENSUS_REPLICAS", 11)) +if MAX_CONSENSUS_REPLICAS < 1: + raise ImproperlyConfigured(f"MAX_CONSENSUS_REPLICAS must be >= 1, got {MAX_CONSENSUS_REPLICAS}") diff --git a/cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_jobs_per_regular_job_and_more.py b/cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_replicas_and_more.py similarity index 87% rename from cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_jobs_per_regular_job_and_more.py rename to cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_replicas_and_more.py index 7819815db4b9..689963ab45cc 100644 --- a/cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_jobs_per_regular_job_and_more.py +++ b/cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_replicas_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.17 on 2025-01-13 14:21 +# Generated by Django 4.2.17 on 2025-01-13 15:51 import cvat.apps.engine.models from django.db import migrations, models @@ -25,8 +25,8 @@ class Migration(migrations.Migration): ), migrations.AddField( model_name="task", - name="consensus_jobs_per_regular_job", - field=models.IntegerField(blank=True, default=0), + name="consensus_replicas", + field=models.IntegerField(default=0), ), migrations.AlterField( model_name="job", diff --git a/cvat/apps/engine/models.py b/cvat/apps/engine/models.py index 0e97a91dc145..b669fa991073 100644 --- a/cvat/apps/engine/models.py +++ b/cvat/apps/engine/models.py @@ -575,7 +575,8 @@ class Task(TimestampedModel): blank=True, on_delete=models.SET_NULL, related_name='+') target_storage = models.ForeignKey('Storage', null=True, default=None, blank=True, on_delete=models.SET_NULL, related_name='+') - consensus_jobs_per_regular_job = models.IntegerField(default=0, blank=True) + consensus_replicas = models.IntegerField(default=0) + "Per job consensus replica count" segment_set: models.manager.RelatedManager[Segment] diff --git a/cvat/apps/engine/serializers.py b/cvat/apps/engine/serializers.py index 85c618e491c5..ddef08cabfa7 100644 --- a/cvat/apps/engine/serializers.py +++ b/cvat/apps/engine/serializers.py @@ -166,7 +166,7 @@ def get_attribute(self, instance): return instance class JobsSummarySerializer(_CollectionSummarySerializer): - count = serializers.IntegerField(source='total_jobs_count', allow_null=True) + count = serializers.IntegerField(source='total_jobs_count', default=0) completed = serializers.IntegerField(source='completed_jobs_count', allow_null=True) validation = serializers.IntegerField(source='validation_jobs_count', allow_null=True) @@ -638,6 +638,9 @@ class JobReadSerializer(serializers.ModelSerializer): target_storage = StorageSerializer(required=False, allow_null=True) source_storage = StorageSerializer(required=False, allow_null=True) parent_job_id = serializers.ReadOnlyField(allow_null=True) + consensus_replicas = serializers.ReadOnlyField( + source='segment.task.consensus_replicas', allow_null=True + ) class Meta: model = models.Job @@ -646,7 +649,9 @@ class Meta: 'start_frame', 'stop_frame', 'data_chunk_size', 'data_compressed_chunk_type', 'data_original_chunk_type', 'created_date', 'updated_date', 'issues', 'labels', 'type', 'organization', - 'target_storage', 'source_storage', 'assignee_updated_date', 'parent_job_id') + 'target_storage', 'source_storage', 'assignee_updated_date', 'parent_job_id', + 'consensus_replicas' + ) read_only_fields = fields def to_representation(self, instance): @@ -2225,7 +2230,7 @@ class TaskReadSerializer(serializers.ModelSerializer): source='data.validation_mode', required=False, allow_null=True, help_text="Describes how the task validation is performed. Configured at task creation" ) - consensus_jobs_per_regular_job = serializers.ReadOnlyField(required=False, allow_null=True) + consensus_enabled = serializers.ReadOnlyField(required=False, allow_null=True) class Meta: model = models.Task @@ -2234,7 +2239,7 @@ class Meta: 'status', 'data_chunk_size', 'data_compressed_chunk_type', 'guide_id', 'data_original_chunk_type', 'size', 'image_quality', 'data', 'dimension', 'subset', 'organization', 'target_storage', 'source_storage', 'jobs', 'labels', - 'assignee_updated_date', 'validation_mode', 'consensus_jobs_per_regular_job', + 'assignee_updated_date', 'validation_mode', 'consensus_enabled', ) read_only_fields = fields extra_kwargs = { @@ -2242,6 +2247,9 @@ class Meta: 'overlap': { 'allow_null': True }, } + def get_consensus_enabled(self, instance: models.Task) -> bool: + return instance.consensus_replicas > 0 + class TaskWriteSerializer(WriteOnceMixin, serializers.ModelSerializer): labels = LabelSerializer(many=True, source='label_set', partial=True, required=False) @@ -2250,21 +2258,38 @@ class TaskWriteSerializer(WriteOnceMixin, serializers.ModelSerializer): project_id = serializers.IntegerField(required=False, allow_null=True) target_storage = StorageSerializer(required=False, allow_null=True) source_storage = StorageSerializer(required=False, allow_null=True) - consensus_jobs_per_regular_job = serializers.IntegerField(required=False, allow_null=True) + consensus_replicas = serializers.IntegerField( + required=False, allow_null=True, default=0, min_value=0, + help_text=textwrap.dedent("""\ + The number of consensus replica jobs for each annotation job. + Configured at task creation + """) + ) class Meta: model = models.Task fields = ( 'url', 'id', 'name', 'project_id', 'owner_id', 'assignee_id', 'bug_tracker', 'overlap', 'segment_size', 'labels', 'subset', - 'target_storage', 'source_storage', 'consensus_jobs_per_regular_job', + 'target_storage', 'source_storage', 'consensus_replicas', ) - write_once_fields = ('overlap', 'segment_size') + write_once_fields = ('overlap', 'segment_size', 'consensus_replicas') def to_representation(self, instance): serializer = TaskReadSerializer(instance, context=self.context) return serializer.data + def validate_consensus_replicas(self, value): + max_replicas = settings.MAX_CONSENSUS_REPLICAS + if value and (value == 1 or value < 0 or value > max_replicas): + raise serializers.ValidationError( + f"Consensus replicas must be 0 " + f"or a positive number more than 1 and less than {max_replicas + 1}, " + f"got {value}" + ) + + return value or 0 + # pylint: disable=no-self-use @transaction.atomic def create(self, validated_data): @@ -2446,11 +2471,6 @@ def validate(self, attrs): if sublabels != target_project_sublabel_names.get(label): raise serializers.ValidationError('All task or project label names must be mapped to the target project') - consensus_jobs_per_regular_job = attrs.get('consensus_jobs_per_regular_job', self.instance.consensus_jobs_per_regular_job if self.instance else None) - - if consensus_jobs_per_regular_job and (consensus_jobs_per_regular_job == 1 or consensus_jobs_per_regular_job < 0 or consensus_jobs_per_regular_job > 10): - raise serializers.ValidationError("Consensus jobs per regular job shouldn't be negative, less than 10 except 1") - return attrs class ProjectReadSerializer(serializers.ModelSerializer): diff --git a/cvat/apps/engine/task.py b/cvat/apps/engine/task.py index 26532f3909f5..f759b9886378 100644 --- a/cvat/apps/engine/task.py +++ b/cvat/apps/engine/task.py @@ -225,7 +225,7 @@ def _create_segments_and_jobs( db_job.make_dirs() # consensus jobs use the same `db_segment` as the regular job, thus data not duplicated in backups, exports - for _ in range(db_task.consensus_jobs_per_regular_job): + for _ in range(db_task.consensus_replicas): consensus_db_job = models.Job(segment=db_segment, parent_job_id=db_job.id, type=models.JobType.CONSENSUS) consensus_db_job.save() consensus_db_job.make_dirs() diff --git a/cvat/schema.yml b/cvat/schema.yml index 4b6e9ddca0da..382a1af8d907 100644 --- a/cvat/schema.yml +++ b/cvat/schema.yml @@ -1804,7 +1804,7 @@ paths: Details about the syntax used can be found at the link: https://jsonlogic.com/ - Available filter_fields: ['task_name', 'project_name', 'assignee', 'state', 'stage', 'id', 'task_id', 'project_id', 'updated_date', 'dimension', 'type']. + Available filter_fields: ['task_name', 'project_name', 'assignee', 'state', 'stage', 'id', 'task_id', 'project_id', 'updated_date', 'dimension', 'type', 'parent_job_id']. schema: type: string - name: org @@ -1829,6 +1829,11 @@ paths: description: Number of results to return per page. schema: type: integer + - name: parent_job_id + in: query + description: A simple equality filter for the parent_job_id field + schema: + type: integer - name: project_id in: query description: A simple equality filter for the project_id field @@ -1851,7 +1856,8 @@ paths: in: query description: 'Which field to use when ordering the results. Available ordering_fields: [''task_name'', ''project_name'', ''assignee'', ''state'', ''stage'', ''id'', - ''task_id'', ''project_id'', ''updated_date'', ''dimension'', ''type'']' + ''task_id'', ''project_id'', ''updated_date'', ''dimension'', ''type'', + ''parent_job_id'']' schema: type: string - name: stage @@ -1891,6 +1897,7 @@ paths: enum: - annotation - ground_truth + - consensus tags: - jobs security: @@ -8335,6 +8342,16 @@ components: format: date-time readOnly: true nullable: true + parent_job_id: + type: integer + nullable: true + readOnly: true + consensus_replicas: + type: integer + maximum: 2147483647 + minimum: -2147483648 + readOnly: true + nullable: true required: - issues - labels @@ -8362,10 +8379,12 @@ components: enum: - annotation - ground_truth + - consensus type: string description: |- * `annotation` - ANNOTATION * `ground_truth` - GROUND_TRUTH + * `consensus` - CONSENSUS JobValidationLayoutRead: type: object properties: @@ -10947,6 +10966,10 @@ components: nullable: true description: Describes how the task validation is performed. Configured at task creation + consensus_enabled: + type: string + readOnly: true + nullable: true required: - jobs - labels @@ -11039,6 +11062,14 @@ components: allOf: - $ref: '#/components/schemas/StorageRequest' nullable: true + consensus_replicas: + type: integer + minimum: 0 + nullable: true + default: 0 + description: | + The number of consensus replica jobs for each annotation job. + Configured at task creation required: - name TasksSummary: From b7f5f8e18723e3c12698bdad663a052940df3bbf Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Mon, 13 Jan 2025 20:34:06 +0200 Subject: [PATCH 07/21] Move to per job replica counts --- cvat-core/src/server-response-types.ts | 3 ++- cvat-core/src/session.ts | 18 +++++++++++++----- .../advanced-configuration-form.tsx | 2 +- cvat-ui/src/components/job-item/job-item.tsx | 19 ++++++------------- cvat-ui/src/components/jobs-page/job-card.tsx | 2 +- cvat-ui/src/components/task-page/details.tsx | 8 ++++---- cvat-ui/src/components/task-page/job-list.tsx | 2 +- cvat/apps/engine/serializers.py | 19 +++++++++++++++---- cvat/schema.yml | 6 +----- 9 files changed, 44 insertions(+), 35 deletions(-) diff --git a/cvat-core/src/server-response-types.ts b/cvat-core/src/server-response-types.ts index e5921e3b7ef6..9218f7fc5617 100644 --- a/cvat-core/src/server-response-types.ts +++ b/cvat-core/src/server-response-types.ts @@ -121,7 +121,7 @@ export interface SerializedTask { subset: string; updated_date: string; url: string; - consensus_replicas: number; + consensus_enabled: boolean; } export interface SerializedJob { @@ -149,6 +149,7 @@ export interface SerializedJob { source_storage: SerializedStorage | null; target_storage: SerializedStorage | null; parent_job_id: number | null; + consensus_replicas: number; } export type AttrInputType = 'select' | 'radio' | 'checkbox' | 'number' | 'text'; diff --git a/cvat-core/src/session.ts b/cvat-core/src/session.ts index ff9475056f2e..ea678b22da01 100644 --- a/cvat-core/src/session.ts +++ b/cvat-core/src/session.ts @@ -506,6 +506,7 @@ export class Job extends Session { source_storage: Storage, target_storage: Storage, parent_job_id: number | null; + consensus_replicas: number; }; constructor(initialData: InitializerType) { super(); @@ -533,6 +534,7 @@ export class Job extends Session { source_storage: undefined, target_storage: undefined, parent_job_id: null, + consensus_replicas: undefined, }; this.#data.id = initialData.id ?? this.#data.id; @@ -548,6 +550,7 @@ export class Job extends Session { this.#data.mode = initialData.mode ?? this.#data.mode; this.#data.created_date = initialData.created_date ?? this.#data.created_date; this.#data.parent_job_id = initialData.parent_job_id ?? this.#data.parent_job_id; + this.#data.consensus_replicas = initialData.consensus_replicas ?? this.#data.consensus_replicas; if (Array.isArray(initialData.labels)) { this.#data.labels = initialData.labels.map((labelData) => { @@ -651,10 +654,14 @@ export class Job extends Session { return this.#data.dimension; } - public get parent_job_id(): number | null { + public get parentJobId(): number | null { return this.#data.parent_job_id; } + public get consensusReplicas(): number | null { + return this.#data.consensus_replicas; + } + public get dataChunkType(): ChunkType { return this.#data.data_compressed_chunk_type; } @@ -757,7 +764,7 @@ export class Task extends Session { public readonly organization: number | null; public readonly progress: { count: number; completed: number }; public readonly jobs: Job[]; - public readonly consensusReplicas: number; + public readonly consensusEnabled: boolean; public readonly startFrame: number; public readonly stopFrame: number; @@ -817,7 +824,7 @@ export class Task extends Session { cloud_storage_id: undefined, sorting_method: undefined, files: undefined, - consensus_replicas: undefined, + consensus_enabled: undefined, validation_mode: null, }; @@ -896,6 +903,7 @@ export class Task extends Session { target_storage: initialData.target_storage, source_storage: initialData.source_storage, parent_job_id: job.parent_job_id, + consensus_replicas: job.consensus_replicas, }); data.jobs.push(jobInstance); } @@ -1003,8 +1011,8 @@ export class Task extends Session { copyData: { get: () => data.copy_data, }, - consensusReplicas: { - get: () => data.consensus_replicas, + consensusEnabled: { + get: () => data.consensus_enabled, }, labels: { get: () => [...data.labels], diff --git a/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx b/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx index 44661c8fd0ee..a923e5ddbf3f 100644 --- a/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx +++ b/cvat-ui/src/components/create-task-page/advanced-configuration-form.tsx @@ -492,7 +492,7 @@ class AdvancedConfigurationForm extends React.PureComponent { {this.renderChunkSize()} - + {this.renderConsensusReplicas()} diff --git a/cvat-ui/src/components/job-item/job-item.tsx b/cvat-ui/src/components/job-item/job-item.tsx index 9565f6e0c9f7..318156c770ce 100644 --- a/cvat-ui/src/components/job-item/job-item.tsx +++ b/cvat-ui/src/components/job-item/job-item.tsx @@ -123,14 +123,11 @@ function JobItem(props: Props): JSX.Element { } const frameCountPercent = ((job.frameCount / (task.size || 1)) * 100).toFixed(0); const frameCountPercentRepresentation = frameCountPercent === '0' ? '<1' : frameCountPercent; - let jobName = `Job #${job.id}`; - if (task.consensusReplicas && job.type !== JobType.GROUND_TRUTH) { - jobName = `Job #${job.id}`; - } + const jobName = `Job #${job.id}`; let consensusJobs: Job[] = []; - if (task.consensusReplicas) { - consensusJobs = task.jobs.filter((eachJob: Job) => eachJob.parent_job_id === job.id).reverse(); + if (task.consensusEnabled) { + consensusJobs = task.jobs.filter((eachJob: Job) => eachJob.parentJobId === job.id).reverse(); } const consensusJobViews: React.JSX.Element[] = consensusJobs.map((eachJob: Job) => ( @@ -149,7 +146,8 @@ function JobItem(props: Props): JSX.Element { Ground truth ); - } else if (job.type === JobType.ANNOTATION && task.consensusReplicas) { + } + if (job.consensusReplicas) { tag = ( Consensus @@ -300,12 +298,7 @@ function JobItem(props: Props): JSX.Element { trigger={['click']} destroyPopupOnHide className='job-actions-menu' - overlay={( - - )} + overlay={} > diff --git a/cvat-ui/src/components/jobs-page/job-card.tsx b/cvat-ui/src/components/jobs-page/job-card.tsx index 6d2593c44ba6..9e35a0625e95 100644 --- a/cvat-ui/src/components/jobs-page/job-card.tsx +++ b/cvat-ui/src/components/jobs-page/job-card.tsx @@ -95,7 +95,7 @@ function JobCardComponent(props: Props): JSX.Element { } + overlay={} > diff --git a/cvat-ui/src/components/task-page/details.tsx b/cvat-ui/src/components/task-page/details.tsx index 73c62e7e8f01..37616f96c616 100644 --- a/cvat-ui/src/components/task-page/details.tsx +++ b/cvat-ui/src/components/task-page/details.tsx @@ -60,7 +60,7 @@ const core = getCore(); interface State { name: string; subset: string; - consensusReplicas: number; + consensusEnabled: boolean; } type Props = DispatchToProps & StateToProps & OwnProps; @@ -72,7 +72,7 @@ class DetailsComponent extends React.PureComponent { this.state = { name: taskInstance.name, subset: taskInstance.subset, - consensusReplicas: taskInstance.consensusReplicas, + consensusEnabled: taskInstance.consensusEnabled, }; } @@ -117,7 +117,7 @@ class DetailsComponent extends React.PureComponent { private renderDescription(): JSX.Element { const { task: taskInstance, onUpdateTask } = this.props; - const { consensusReplicas } = this.state; + const { consensusEnabled } = this.state; const owner = taskInstance.owner ? taskInstance.owner.username : null; const assignee = taskInstance.assignee ? taskInstance.assignee : null; const created = moment(taskInstance.createdDate).format('MMMM Do YYYY'); @@ -142,7 +142,7 @@ class DetailsComponent extends React.PureComponent { )} - {consensusReplicas > 0 && Consensus} + {consensusEnabled && Consensus} Assigned to diff --git a/cvat-ui/src/components/task-page/job-list.tsx b/cvat-ui/src/components/task-page/job-list.tsx index c6f52cc662d9..60853306f573 100644 --- a/cvat-ui/src/components/task-page/job-list.tsx +++ b/cvat-ui/src/components/task-page/job-list.tsx @@ -60,7 +60,7 @@ function setUpJobsList(jobs: Job[], query: JobsQuery): Job[] { } // consensus jobs will be under the collapse view - result = result.filter((job) => job.parent_job_id === null); + result = result.filter((job) => job.parentJobId === null); return result; } diff --git a/cvat/apps/engine/serializers.py b/cvat/apps/engine/serializers.py index ddef08cabfa7..7353dde7ded1 100644 --- a/cvat/apps/engine/serializers.py +++ b/cvat/apps/engine/serializers.py @@ -638,9 +638,7 @@ class JobReadSerializer(serializers.ModelSerializer): target_storage = StorageSerializer(required=False, allow_null=True) source_storage = StorageSerializer(required=False, allow_null=True) parent_job_id = serializers.ReadOnlyField(allow_null=True) - consensus_replicas = serializers.ReadOnlyField( - source='segment.task.consensus_replicas', allow_null=True - ) + consensus_replicas = serializers.IntegerField(read_only=True) class Meta: model = models.Job @@ -656,9 +654,15 @@ class Meta: def to_representation(self, instance): data = super().to_representation(instance) + if instance.segment.type == models.SegmentType.SPECIFIC_FRAMES: data['data_compressed_chunk_type'] = models.DataChoice.IMAGESET + if instance.type == models.JobType.ANNOTATION: + data['consensus_replicas'] = instance.segment.task.consensus_replicas + else: + data['consensus_replicas'] = 0 + if request := self.context.get('request'): perm = TaskPermission.create_scope_view(request, instance.segment.task) result = perm.check_access() @@ -2230,7 +2234,9 @@ class TaskReadSerializer(serializers.ModelSerializer): source='data.validation_mode', required=False, allow_null=True, help_text="Describes how the task validation is performed. Configured at task creation" ) - consensus_enabled = serializers.ReadOnlyField(required=False, allow_null=True) + consensus_enabled = serializers.BooleanField( + source='get_consensus_enabled', required=False, read_only=True + ) class Meta: model = models.Task @@ -2250,6 +2256,11 @@ class Meta: def get_consensus_enabled(self, instance: models.Task) -> bool: return instance.consensus_replicas > 0 + def to_representation(self, instance): + representation = super().to_representation(instance) + representation['consensus_enabled'] = self.get_consensus_enabled(instance) + return representation + class TaskWriteSerializer(WriteOnceMixin, serializers.ModelSerializer): labels = LabelSerializer(many=True, source='label_set', partial=True, required=False) diff --git a/cvat/schema.yml b/cvat/schema.yml index 382a1af8d907..2c5e306673e3 100644 --- a/cvat/schema.yml +++ b/cvat/schema.yml @@ -8348,10 +8348,7 @@ components: readOnly: true consensus_replicas: type: integer - maximum: 2147483647 - minimum: -2147483648 readOnly: true - nullable: true required: - issues - labels @@ -10967,9 +10964,8 @@ components: description: Describes how the task validation is performed. Configured at task creation consensus_enabled: - type: string + type: boolean readOnly: true - nullable: true required: - jobs - labels From 68c51a3748eb8fa55382c0bfc8012bbfeb48fea8 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Mon, 13 Jan 2025 20:40:00 +0200 Subject: [PATCH 08/21] Revert some changes --- cvat-core/src/session.ts | 1 + cvat/apps/dataset_manager/bindings.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat-core/src/session.ts b/cvat-core/src/session.ts index ea678b22da01..5cb8c8bb74e4 100644 --- a/cvat-core/src/session.ts +++ b/cvat-core/src/session.ts @@ -508,6 +508,7 @@ export class Job extends Session { parent_job_id: number | null; consensus_replicas: number; }; + constructor(initialData: InitializerType) { super(); diff --git a/cvat/apps/dataset_manager/bindings.py b/cvat/apps/dataset_manager/bindings.py index 28b91f509e0f..08c2f51adad6 100644 --- a/cvat/apps/dataset_manager/bindings.py +++ b/cvat/apps/dataset_manager/bindings.py @@ -780,7 +780,6 @@ def _init_meta(self): ("start_frame", str(self._db_data.start_frame + db_segment.start_frame * self._frame_step)), ("stop_frame", str(self._db_data.start_frame + db_segment.stop_frame * self._frame_step)), ("frame_filter", self._db_data.frame_filter), - ("parent_job_id", str(self._db_job.parent_job_id)), ("segments", [ ("segment", OrderedDict([ ("id", str(db_segment.id)), From 03295964609885560ee7a29e0f0a6928d325f260 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Mon, 13 Jan 2025 20:41:06 +0200 Subject: [PATCH 09/21] Update changelog --- changelog.d/20250113_203431_mzhiltso_consensus_tasks.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog.d/20250113_203431_mzhiltso_consensus_tasks.md diff --git a/changelog.d/20250113_203431_mzhiltso_consensus_tasks.md b/changelog.d/20250113_203431_mzhiltso_consensus_tasks.md new file mode 100644 index 000000000000..ac0d6c157c91 --- /dev/null +++ b/changelog.d/20250113_203431_mzhiltso_consensus_tasks.md @@ -0,0 +1,4 @@ +### Added + +- An option to create tasks with consensus jobs + () From ab04a6d26d12912a4517996a20a5980988bd4c74 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Tue, 14 Jan 2025 12:18:11 +0200 Subject: [PATCH 10/21] Apply isort --- .../0087_job_parent_job_task_consensus_replicas_and_more.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_replicas_and_more.py b/cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_replicas_and_more.py index 689963ab45cc..dca5852a161a 100644 --- a/cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_replicas_and_more.py +++ b/cvat/apps/engine/migrations/0087_job_parent_job_task_consensus_replicas_and_more.py @@ -1,8 +1,9 @@ # Generated by Django 4.2.17 on 2025-01-13 15:51 -import cvat.apps.engine.models -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models + +import cvat.apps.engine.models class Migration(migrations.Migration): From 3d7c1a2195ad40d498dd140dc10afd8db9fd8ec9 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Tue, 14 Jan 2025 12:20:38 +0200 Subject: [PATCH 11/21] Fix double assignment --- cvat-ui/src/actions/tasks-actions.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts index faad9e67f0a0..b2af4d552dab 100644 --- a/cvat-ui/src/actions/tasks-actions.ts +++ b/cvat-ui/src/actions/tasks-actions.ts @@ -224,7 +224,6 @@ ThunkAction { sorting_method: data.advanced.sortingMethod, source_storage: new Storage(data.advanced.sourceStorage || { location: StorageLocation.LOCAL }).toJSON(), target_storage: new Storage(data.advanced.targetStorage || { location: StorageLocation.LOCAL }).toJSON(), - consensus_replicas: data.advanced.consensusReplicas, }; if (data.projectId) { @@ -263,6 +262,9 @@ ThunkAction { if (data.cloudStorageId) { description.cloud_storage_id = data.cloudStorageId; } + if (data.advanced.consensusReplicas) { + description.consensus_replicas = +data.advanced.consensusReplicas; + } let extras = {}; @@ -276,9 +278,6 @@ ThunkAction { }, }; } - if (data.advanced.consensusReplicas) { - description.consensus_replicas = +data.advanced.consensusReplicas; - } const taskInstance = new cvat.classes.Task(description); taskInstance.clientFiles = data.files.local; From 3226dd7269b0297e82a3b87d969b5c3c644c8ff5 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Tue, 14 Jan 2025 12:45:17 +0200 Subject: [PATCH 12/21] Refactor --- cvat-ui/src/components/job-item/job-item.tsx | 13 ++++++------- cvat-ui/src/reducers/index.ts | 2 +- cvat-ui/src/reducers/jobs-reducer.ts | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/cvat-ui/src/components/job-item/job-item.tsx b/cvat-ui/src/components/job-item/job-item.tsx index 318156c770ce..f11b67edf894 100644 --- a/cvat-ui/src/components/job-item/job-item.tsx +++ b/cvat-ui/src/components/job-item/job-item.tsx @@ -125,11 +125,11 @@ function JobItem(props: Props): JSX.Element { const frameCountPercentRepresentation = frameCountPercent === '0' ? '<1' : frameCountPercent; const jobName = `Job #${job.id}`; - let consensusJobs: Job[] = []; + let childJobs: Job[] = []; if (task.consensusEnabled) { - consensusJobs = task.jobs.filter((eachJob: Job) => eachJob.parentJobId === job.id).reverse(); + childJobs = task.jobs.filter((eachJob: Job) => eachJob.parentJobId === job.id).reverse(); } - const consensusJobViews: React.JSX.Element[] = consensusJobs.map((eachJob: Job) => ( + const consensusJobViews: React.JSX.Element[] = childJobs.map((eachJob: Job) => ( )); @@ -146,8 +146,7 @@ function JobItem(props: Props): JSX.Element { Ground truth ); - } - if (job.consensusReplicas) { + } else if (job.consensusReplicas) { tag = ( Consensus @@ -302,7 +301,7 @@ function JobItem(props: Props): JSX.Element { > - {consensusJobs.length > 0 && ( + {childJobs.length > 0 && ( {`${consensusJobs.length} Consensus Jobs`}, + label: {`${childJobs.length} Replicas`}, children: consensusJobViews, }, ]} diff --git a/cvat-ui/src/reducers/index.ts b/cvat-ui/src/reducers/index.ts index 96cacb3003fa..8304b4d0a8a8 100644 --- a/cvat-ui/src/reducers/index.ts +++ b/cvat-ui/src/reducers/index.ts @@ -90,7 +90,7 @@ export interface JobsState { }; }; regularJobViewUncollapse: { - [tid: number]: boolean; + [jobId: number]: boolean; }; } diff --git a/cvat-ui/src/reducers/jobs-reducer.ts b/cvat-ui/src/reducers/jobs-reducer.ts index a448c51765ab..487265f0c5e1 100644 --- a/cvat-ui/src/reducers/jobs-reducer.ts +++ b/cvat-ui/src/reducers/jobs-reducer.ts @@ -166,23 +166,23 @@ export default (state: JobsState = defaultState, action: JobsActions): JobsState } case JobsActionTypes.COLLAPSE_REGULAR_JOB: { const { jobID } = action.payload; - state.regularJobViewUncollapse[jobID] = true; return { ...state, regularJobViewUncollapse: { ...state.regularJobViewUncollapse, + [jobID]: true, }, }; } case JobsActionTypes.UNCOLLAPSE_REGULAR_JOB: { const { jobID } = action.payload; - state.regularJobViewUncollapse[jobID] = false; return { ...state, regularJobViewUncollapse: { ...state.regularJobViewUncollapse, + [jobID]: false, }, }; } From 824cf8250028b4681e60b999ffd60636286adb2b Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Tue, 14 Jan 2025 13:56:02 +0200 Subject: [PATCH 13/21] Fix backups --- cvat/apps/engine/backup.py | 64 ++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/cvat/apps/engine/backup.py b/cvat/apps/engine/backup.py index 6b442be89fef..fef899b1b881 100644 --- a/cvat/apps/engine/backup.py +++ b/cvat/apps/engine/backup.py @@ -11,6 +11,7 @@ import tempfile import uuid from collections.abc import Collection, Iterable +from copy import deepcopy from enum import Enum from logging import Logger from tempfile import NamedTemporaryFile @@ -322,12 +323,14 @@ def _prepare_tracks(tracks, parent_label=''): return annotations def _get_db_jobs(self): - if self._db_task: - db_segments = list(self._db_task.segment_set.all().prefetch_related('job_set')) - db_segments.sort(key=lambda i: i.job_set.first().id) - db_jobs = (job for s in db_segments for job in s.job_set.all()) - return db_jobs - return () + if not self._db_task: + return + + db_segments = list(self._db_task.segment_set.all().prefetch_related('job_set')) + db_segments.sort(key=lambda i: i.job_set.first().id) + + for db_segment in db_segments: + yield from sorted(db_segment.job_set.all(), key=lambda db_job: db_job.id) class _ExporterBase(): def __init__(self, *args, **kwargs): @@ -430,7 +433,11 @@ def serialize_task(): task_labels = LabelSerializer(self._db_task.get_labels(prefetch=True), many=True) - task = self._prepare_task_meta(task_serializer.data) + serialized_task = task_serializer.data + if serialized_task.pop('consensus_enabled', False): + serialized_task['consensus_replicas'] = self._db_task.consensus_replicas + + task = self._prepare_task_meta(serialized_task) task['labels'] = [self._prepare_label_meta(l) for l in task_labels.data if not l['has_parent']] for label in task['labels']: label['attributes'] = [self._prepare_attribute_meta(a) for a in label['attributes']] @@ -438,34 +445,39 @@ def serialize_task(): return task def serialize_segment(db_segment): - segments = [] - db_jobs = db_segment.job_set.all() - for db_job in db_jobs: - job_serializer = SimpleJobSerializer(db_job) - for field in ('url', 'assignee'): - job_serializer.fields.pop(field) - job_data = self._prepare_job_meta(job_serializer.data) - - segment_serializer = SegmentSerializer(db_segment) - segment_serializer.fields.pop('jobs') - segment = segment_serializer.data - segment_type = segment.pop("type") - segment.update(job_data) + segment_serializer = SegmentSerializer(db_segment) + segment_serializer.fields.pop('jobs') + serialized_segment = segment_serializer.data + segment_type = serialized_segment.pop("type") if ( self._db_task.segment_size == 0 and segment_type == models.SegmentType.RANGE or self._db_data.validation_mode == models.ValidationMode.GT_POOL ): - segment.update(serialize_segment_file_names(db_segment)) - - segments.append(segment) + serialized_segment.update(serialize_segment_file_names(db_segment)) - return segments + return serialized_segment def serialize_jobs(): db_segments = list(self._db_task.segment_set.all()) db_segments.sort(key=lambda i: i.job_set.first().id) - return (serialized_job for s in db_segments for serialized_job in serialize_segment(s)) + + serialized_jobs = [] + for db_segment in db_segments: + serialized_segment = serialize_segment(db_segment) + + db_jobs = list(db_segment.job_set.all()) + db_jobs.sort(key=lambda v: v.id) + for db_job in db_jobs: + job_serializer = SimpleJobSerializer(db_job) + for field in ('url', 'assignee'): + job_serializer.fields.pop(field) + + serialized_job = self._prepare_job_meta(job_serializer.data) + serialized_job.update(deepcopy(serialized_segment)) + serialized_jobs.append(serialized_job) + + return serialized_jobs def serialize_segment_file_names(db_segment: models.Segment): if self._db_task.mode == 'annotation': @@ -763,7 +775,7 @@ def _import_task(self): job_file_mapping = None if data.pop('custom_segments', False): job_file_mapping = self._parse_segment_frames(jobs=[ - v for v in jobs if v.get('type') != models.JobType.GROUND_TRUTH + v for v in jobs if v.get('type') == models.JobType.ANNOTATION ]) for d in [self._manifest, data]: From 02d78b902c500dceedcec7f56efbec86cfcd046b Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Tue, 14 Jan 2025 17:11:03 +0200 Subject: [PATCH 14/21] Fix field sending --- cvat-core/src/session-implementation.ts | 4 ++-- cvat-core/src/session.ts | 2 +- cvat-ui/src/actions/tasks-actions.ts | 18 ++++++++++-------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/cvat-core/src/session-implementation.ts b/cvat-core/src/session-implementation.ts index b5f463c32e21..db306b4f42cb 100644 --- a/cvat-core/src/session-implementation.ts +++ b/cvat-core/src/session-implementation.ts @@ -748,8 +748,8 @@ export function implementTask(Task: typeof TaskClass): typeof TaskClass { taskSpec.source_storage = this.sourceStorage.toJSON(); } - if (this.consensusReplicas) { - taskSpec.consensus_replicas = this.consensusReplicas; + if (fields.consensus_replicas) { + taskSpec.consensus_replicas = fields.consensus_replicas; } const taskDataSpec = { diff --git a/cvat-core/src/session.ts b/cvat-core/src/session.ts index 5cb8c8bb74e4..2ca9515c5cb4 100644 --- a/cvat-core/src/session.ts +++ b/cvat-core/src/session.ts @@ -659,7 +659,7 @@ export class Job extends Session { return this.#data.parent_job_id; } - public get consensusReplicas(): number | null { + public get consensusReplicas(): number { return this.#data.consensus_replicas; } diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts index b2af4d552dab..41f47267c39a 100644 --- a/cvat-ui/src/actions/tasks-actions.ts +++ b/cvat-ui/src/actions/tasks-actions.ts @@ -266,19 +266,21 @@ ThunkAction { description.consensus_replicas = +data.advanced.consensusReplicas; } - let extras = {}; + const extras: Record = {}; if (data.quality.validationMode !== ValidationMode.NONE) { - extras = { - validation_params: { - mode: data.quality.validationMode, - frame_selection_method: data.quality.frameSelectionMethod, - frame_share: data.quality.validationFramesPercent, - frames_per_job_share: data.quality.validationFramesPerJobPercent, - }, + extras.validation_params = { + mode: data.quality.validationMode, + frame_selection_method: data.quality.frameSelectionMethod, + frame_share: data.quality.validationFramesPercent, + frames_per_job_share: data.quality.validationFramesPerJobPercent, }; } + if (data.advanced.consensusReplicas) { + extras.consensus_replicas = description.consensus_replicas; + } + const taskInstance = new cvat.classes.Task(description); taskInstance.clientFiles = data.files.local; taskInstance.serverFiles = data.files.share.concat(data.files.cloudStorage); From b8b096cc62e2b1f3941e734c7dbde4e80f9f46b6 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Tue, 14 Jan 2025 19:09:12 +0200 Subject: [PATCH 15/21] Add task creation and data access tests --- tests/python/rest_api/test_tasks.py | 124 ++++++++++++++++++++++++++-- tests/python/rest_api/utils.py | 15 +++- 2 files changed, 130 insertions(+), 9 deletions(-) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index e226935d82d0..445b893d9c6d 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -73,6 +73,7 @@ export_task_backup, export_task_dataset, parse_frame_step, + unique, wait_until_task_is_created, ) @@ -2680,6 +2681,65 @@ def test_can_create_task_with_validation_and_cloud_data( for img, img_meta in zip(chunk_images.values(), job_meta.frames): assert (img.shape[0], img.shape[1]) == (img_meta.height, img_meta.width) + def test_can_create_task_with_consensus(self, request: pytest.FixtureRequest): + segment_size = 2 + regular_job_count = 2 + replication = 2 + images = generate_image_files(segment_size * regular_job_count) + resulting_task_size = len(images) + + task_params = { + "name": request.node.name, + "labels": [{"name": "a"}], + "segment_size": segment_size, + "consensus_replicas": replication, + } + + data_params = { + "image_quality": 70, + "client_files": images, + } + + task_id, _ = create_task(self._USERNAME, spec=task_params, data=data_params) + + with make_api_client(self._USERNAME) as api_client: + (task, _) = api_client.tasks_api.retrieve(task_id) + (task_meta, _) = api_client.tasks_api.retrieve_data_meta(task_id) + jobs = get_paginated_collection(api_client.jobs_api.list_endpoint, task_id=task_id) + + annotation_job_metas = { + job.id: json.loads(api_client.jobs_api.retrieve_data_meta(job.id)[1].data) + for job in jobs + if job.type == "annotation" + } + consensus_job_metas = { + job.id: json.loads(api_client.jobs_api.retrieve_data_meta(job.id)[1].data) + for job in jobs + if job.type == "consensus" + } + + assert task.segment_size == segment_size + assert task.size == resulting_task_size + assert task_meta.size == resulting_task_size + + assert len(jobs) == regular_job_count * (1 + replication) + assert len(annotation_job_metas) == regular_job_count + assert len(consensus_job_metas) == regular_job_count * replication + + for annotation_job in (j for j in jobs if j.type == "annotation"): + assert annotation_job_metas[annotation_job.id]["size"] == segment_size + + job_replicas = [j for j in jobs if j.parent_job_id == annotation_job.id] + assert len(job_replicas) == replication + + for replica in job_replicas: + assert ( + DeepDiff( + consensus_job_metas[replica.id], annotation_job_metas[annotation_job.id] + ) + == {} + ) + class _SourceDataType(Enum): images = "images" @@ -2762,14 +2822,15 @@ def _uploaded_images_task_fxt_base( stop_frame: Optional[int] = None, step: Optional[int] = None, segment_size: Optional[int] = None, + job_replication: Optional[int] = None, **data_kwargs, ) -> Generator[tuple[_ImagesTaskSpec, int], None, None]: task_params = { "name": f"{request.node.name}[{request.fixturename}]", "labels": [{"name": "a"}], + **({"segment_size": segment_size} if segment_size else {}), + **({"consensus_replicas": job_replication} if job_replication else {}), } - if segment_size: - task_params["segment_size"] = segment_size assert bool(image_files) ^ bool( frame_count @@ -2841,6 +2902,14 @@ def fxt_uploaded_images_task_with_segments_start_stop_step( step=step, ) + @pytest.fixture(scope="class") + def fxt_uploaded_images_task_with_segments_and_consensus( + self, request: pytest.FixtureRequest + ) -> Generator[tuple[_TaskSpec, int], None, None]: + yield from self._uploaded_images_task_fxt_base( + request=request, segment_size=4, job_replication=2 + ) + def _uploaded_images_task_with_honeypots_and_segments_base( self, request: pytest.FixtureRequest, @@ -2967,6 +3036,7 @@ def _uploaded_images_task_with_gt_and_segments_base( start_frame: Optional[int] = None, step: Optional[int] = None, frame_selection_method: str = "random_uniform", + job_replication: Optional[int] = None, ) -> Generator[tuple[_TaskSpec, int], None, None]: used_frames_count = 16 total_frame_count = (start_frame or 0) + used_frames_count * (step or 1) @@ -3016,6 +3086,15 @@ def _uploaded_images_task_with_gt_and_segments_base( start_frame=start_frame, step=step, validation_params=validation_params, + job_replication=job_replication, + ) + + @pytest.fixture(scope="class") + def fxt_uploaded_images_task_with_gt_and_segments_and_consensus( + self, request: pytest.FixtureRequest + ) -> Generator[tuple[_TaskSpec, int], None, None]: + yield from self._uploaded_images_task_with_gt_and_segments_base( + request=request, job_replication=2 ) @fixture(scope="class") @@ -3182,17 +3261,19 @@ def _get_job_abs_frame_set(self, job_meta: models.DataMetaRead) -> Sequence[int] ] _tasks_with_simple_gt_job_cases = [ - fixture_ref("fxt_uploaded_images_task_with_gt_and_segments_start_step") + fixture_ref("fxt_uploaded_images_task_with_gt_and_segments_start_step"), + fixture_ref("fxt_uploaded_images_task_with_gt_and_segments_and_consensus"), ] - _tasks_with_simple_gt_job_cases = [ - fixture_ref("fxt_uploaded_images_task_with_gt_and_segments_start_step") + _tasks_with_consensus_cases = [ + fixture_ref("fxt_uploaded_images_task_with_segments_and_consensus"), + fixture_ref("fxt_uploaded_images_task_with_gt_and_segments_and_consensus"), ] # Keep in mind that these fixtures are generated eagerly # (before each depending test or group of tests), # e.g. a failing task creation in one the fixtures will fail all the depending tests cases. - _all_task_cases = ( + _all_task_cases = unique( [ fixture_ref("fxt_uploaded_images_task"), fixture_ref("fxt_uploaded_images_task_with_segments"), @@ -3203,6 +3284,8 @@ def _get_job_abs_frame_set(self, job_meta: models.DataMetaRead) -> Sequence[int] ] + _tasks_with_honeypots_cases + _tasks_with_simple_gt_job_cases + + _tasks_with_consensus_cases, + key=lambda fxt_ref: fxt_ref.fixture, ) @parametrize("task_spec, task_id", _all_task_cases) @@ -3461,6 +3544,35 @@ def test_can_get_honeypot_gt_job_meta(self, task_spec: _TaskSpec, task_id: int): else: assert len(job_meta.frames) == job_meta.size + @parametrize("task_spec, task_id", _tasks_with_consensus_cases) + def test_can_get_consensus_replica_job_meta(self, task_spec: _TaskSpec, task_id: int): + with make_api_client(self._USERNAME) as api_client: + jobs = sorted( + get_paginated_collection(api_client.jobs_api.list_endpoint, task_id=task_id), + key=lambda j: j.start_frame, + ) + + # Only annotation jobs can have replicas + annotation_jobs = [j for j in jobs if j.type == "annotation"] + assert ( + len([j for j in jobs if j.type == "consensus"]) + == len(annotation_jobs) * task_spec.consensus_replicas + ) + + for job in annotation_jobs: + annotation_job_meta = json.loads( + api_client.jobs_api.retrieve_data_meta(job.id)[1].data + ) + + replicas = [j for j in jobs if j.type == "consensus" if j.parent_job_id == job.id] + assert len(replicas) == task_spec.consensus_replicas + + for replica_job in replicas: + replica_job_meta = json.loads( + api_client.jobs_api.retrieve_data_meta(replica_job.id)[1].data + ) + assert DeepDiff(annotation_job_meta, replica_job_meta) == {} + @parametrize("task_spec, task_id", _all_task_cases) def test_can_get_job_frames(self, task_spec: _TaskSpec, task_id: int): with make_api_client(self._USERNAME) as api_client: diff --git a/tests/python/rest_api/utils.py b/tests/python/rest_api/utils.py index 8d5032998358..edf39430885d 100644 --- a/tests/python/rest_api/utils.py +++ b/tests/python/rest_api/utils.py @@ -1,14 +1,14 @@ -# Copyright (C) 2022-2023 CVAT.ai Corporation +# Copyright (C) 2022-2025 CVAT.ai Corporation # # SPDX-License-Identifier: MIT import json from abc import ABCMeta, abstractmethod -from collections.abc import Iterator, Sequence +from collections.abc import Hashable, Iterator, Sequence from copy import deepcopy from http import HTTPStatus from time import sleep -from typing import Any, Callable, Optional, Union +from typing import Any, Callable, Iterable, Optional, TypeVar, Union import requests from cvat_sdk.api_client import apis, models @@ -605,3 +605,12 @@ def parse_frame_step(frame_filter: str) -> int: def calc_end_frame(start_frame: int, stop_frame: int, frame_step: int) -> int: return stop_frame - ((stop_frame - start_frame) % frame_step) + frame_step + + +_T = TypeVar("_T") + + +def unique( + it: Iterator[_T] | Iterable[_T], *, key: Callable[[_T], Hashable] = None +) -> Iterable[_T]: + return {key(v): v for v in it}.values() From 8f8a406b05d8e6ffb5fdfa516c47219945ba3d74 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Tue, 14 Jan 2025 19:34:04 +0200 Subject: [PATCH 16/21] Update test assets --- tests/python/shared/assets/annotations.json | 442 ++++++++ .../shared/assets/cvat_db/cvat_data.tar.bz2 | Bin 89057 -> 89295 bytes tests/python/shared/assets/cvat_db/data.json | 953 ++++++++++++++++-- tests/python/shared/assets/jobs.json | 466 ++++++++- tests/python/shared/assets/labels.json | 46 +- .../shared/assets/quality_settings.json | 71 +- tests/python/shared/assets/tasks.json | 79 +- 7 files changed, 1968 insertions(+), 89 deletions(-) diff --git a/tests/python/shared/assets/annotations.json b/tests/python/shared/assets/annotations.json index 39dee6e3279c..e477df05882c 100644 --- a/tests/python/shared/assets/annotations.json +++ b/tests/python/shared/assets/annotations.json @@ -5245,6 +5245,316 @@ "tags": [], "tracks": [], "version": 0 + }, + "42": { + "shapes": [ + { + "attributes": [ + { + "spec_id": 16, + "value": "j1 frame 1 (32) ann 1" + } + ], + "elements": [], + "frame": 0, + "group": 0, + "id": 199, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 260.20437641345416, + 64.96839991694105, + 339.7988160670684, + 103.23495744271713 + ], + "rotation": 0.0, + "source": "manual", + "type": "rectangle", + "z_order": 0 + } + ], + "tags": [], + "tracks": [], + "version": 0 + }, + "43": { + "shapes": [ + { + "attributes": [ + { + "spec_id": 16, + "value": "j1 r1 frame 1 (32) ann 1" + } + ], + "elements": [], + "frame": 0, + "group": 0, + "id": 200, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 236.47911074747208, + 63.43773761591001, + 335.20682916397527, + 123.1335673561207 + ], + "rotation": 0.0, + "source": "manual", + "type": "rectangle", + "z_order": 0 + } + ], + "tags": [], + "tracks": [], + "version": 0 + }, + "44": { + "shapes": [], + "tags": [], + "tracks": [], + "version": 0 + }, + "45": { + "shapes": [], + "tags": [], + "tracks": [], + "version": 0 + }, + "46": { + "shapes": [ + { + "attributes": [ + { + "spec_id": 16, + "value": "j2 frame 2 (32) ann 1" + } + ], + "elements": [], + "frame": 6, + "group": 0, + "id": 194, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 472.58377068151276, + 63.05507204065361, + 535.3409250237855, + 110.50560337261595 + ], + "rotation": 0.0, + "source": "manual", + "type": "rectangle", + "z_order": 0 + } + ], + "tags": [], + "tracks": [], + "version": 0 + }, + "47": { + "shapes": [ + { + "attributes": [ + { + "spec_id": 16, + "value": "j2 r1 frame 1 (35) ann 1" + } + ], + "elements": [], + "frame": 5, + "group": 0, + "id": 195, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 183.389531668141, + 181.09253910664302, + 247.88201512555497, + 226.14893165908325 + ], + "rotation": 0.0, + "source": "manual", + "type": "rectangle", + "z_order": 0 + }, + { + "attributes": [ + { + "spec_id": 16, + "value": "j2 r1 frame 1 (35) ann 2" + } + ], + "elements": [], + "frame": 5, + "group": 0, + "id": 196, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 344.1790109729245, + 311.8444233764694, + 391.8857795578606, + 376.3369068338834 + ], + "rotation": 0.0, + "source": "manual", + "type": "rectangle", + "z_order": 0 + } + ], + "tags": [], + "tracks": [], + "version": 0 + }, + "48": { + "shapes": [], + "tags": [], + "tracks": [], + "version": 0 + }, + "49": { + "shapes": [ + { + "attributes": [ + { + "spec_id": 16, + "value": "j2 r3 frame 1 (35) ann 1" + } + ], + "elements": [], + "frame": 5, + "group": 0, + "id": 197, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 169.25419282815892, + 210.24667546410456, + 239.0474283505664, + 239.4008118215661 + ], + "rotation": 0.0, + "source": "manual", + "type": "rectangle", + "z_order": 0 + }, + { + "attributes": [ + { + "spec_id": 16, + "value": "j2 r3 frame 1 (35) ann 2" + } + ], + "elements": [], + "frame": 5, + "group": 0, + "id": 198, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 336.22788287543517, + 361.3181093164021, + 398.9534489778507, + 424.9271340963169 + ], + "rotation": 0.0, + "source": "manual", + "type": "rectangle", + "z_order": 0 + } + ], + "tags": [], + "tracks": [], + "version": 0 + }, + "50": { + "shapes": [ + { + "attributes": [ + { + "spec_id": 16, + "value": "gt job frame 1 (32) ann 1" + } + ], + "elements": [], + "frame": 8, + "group": 0, + "id": 191, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 160.71132684643453, + 59.61108186333331, + 254.8470583598446, + 111.65360009838878 + ], + "rotation": 0.0, + "source": "Ground truth", + "type": "rectangle", + "z_order": 0 + }, + { + "attributes": [ + { + "spec_id": 16, + "value": "gt job frame 1 (32) ann 2" + } + ], + "elements": [], + "frame": 8, + "group": 0, + "id": 192, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 434.6998787309949, + 78.74436062622226, + 502.0490199763608, + 127.72555425921564 + ], + "rotation": 0.0, + "source": "Ground truth", + "type": "rectangle", + "z_order": 0 + }, + { + "attributes": [ + { + "spec_id": 16, + "value": "gt job frame 2 (35) ann 1" + } + ], + "elements": [], + "frame": 9, + "group": 0, + "id": 193, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 245.67336843180783, + 162.98163621791718, + 334.90269485918907, + 215.98915686784676 + ], + "rotation": 0.0, + "source": "Ground truth", + "type": "rectangle", + "z_order": 0 + } + ], + "tags": [], + "tracks": [], + "version": 0 } }, "task": { @@ -8770,6 +9080,138 @@ "tags": [], "tracks": [], "version": 0 + }, + "30": { + "shapes": [ + { + "attributes": [ + { + "spec_id": 16, + "value": "j2 frame 2 (32) ann 1" + } + ], + "elements": [], + "frame": 6, + "group": 0, + "id": 194, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 472.58377068151276, + 63.05507204065361, + 535.3409250237855, + 110.50560337261595 + ], + "rotation": 0.0, + "source": "manual", + "type": "rectangle", + "z_order": 0 + }, + { + "attributes": [ + { + "spec_id": 16, + "value": "j1 frame 1 (32) ann 1" + } + ], + "elements": [], + "frame": 0, + "group": 0, + "id": 199, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 260.20437641345416, + 64.96839991694105, + 339.7988160670684, + 103.23495744271713 + ], + "rotation": 0.0, + "source": "manual", + "type": "rectangle", + "z_order": 0 + }, + { + "attributes": [ + { + "spec_id": 16, + "value": "gt job frame 1 (32) ann 1" + } + ], + "elements": [], + "frame": 8, + "group": 0, + "id": 191, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 160.71132684643453, + 59.61108186333331, + 254.8470583598446, + 111.65360009838878 + ], + "rotation": 0.0, + "source": "Ground truth", + "type": "rectangle", + "z_order": 0 + }, + { + "attributes": [ + { + "spec_id": 16, + "value": "gt job frame 1 (32) ann 2" + } + ], + "elements": [], + "frame": 8, + "group": 0, + "id": 192, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 434.6998787309949, + 78.74436062622226, + 502.0490199763608, + 127.72555425921564 + ], + "rotation": 0.0, + "source": "Ground truth", + "type": "rectangle", + "z_order": 0 + }, + { + "attributes": [ + { + "spec_id": 16, + "value": "gt job frame 2 (35) ann 1" + } + ], + "elements": [], + "frame": 9, + "group": 0, + "id": 193, + "label_id": 78, + "occluded": false, + "outside": false, + "points": [ + 245.67336843180783, + 162.98163621791718, + 334.90269485918907, + 215.98915686784676 + ], + "rotation": 0.0, + "source": "Ground truth", + "type": "rectangle", + "z_order": 0 + } + ], + "tags": [], + "tracks": [], + "version": 0 } } } \ No newline at end of file diff --git a/tests/python/shared/assets/cvat_db/cvat_data.tar.bz2 b/tests/python/shared/assets/cvat_db/cvat_data.tar.bz2 index 4976f9dff92e3545d2e8836ea3e2895f9a61b3ae..1a92a03399876b3373a89b6a721bee7abcd8e288 100644 GIT binary patch literal 89295 zcmagFbxhsQ6E6J0-Q5odw<1M~+ri!4-JukBKWK4xDHNx;Q=H;noI;DcJMZ_Go4kMB z**_~&T&184ynaQk#zAZnexE{TR9&+hdMA%i!_y1@4gHGVm^AHN+^Fn1l1!;=` zj)>XX+^FaE4=+A90L&v0m9Pi2>G{xBh4I+{j!;F%%F#uluIS9WZgW-gG(s#m}!z%f%lMxv$f*-tRdfs%wWa zu*5uXKkK8Q0M-M}DSrb1;`8xzk4P|J62N~0AU<^#hyfJ)YlhSHfM1>441m^kpYaCZALXAM+1Ov6F5WmQW@ zQLNmy{e65uCPGzKL_xUYy)=M|q9aR&XXXiYb zS_V0r(?-K(=N?}0zl=E7bnxJKT$~mrE6?Um=Cmi7ooe=Tx-}+S?F~_jtt%ZTveyus zHrCsQjW#@L6df0E?dYchg#X>}t%TMHoXxT19#>7V_)UA>D+z&o~4pkt7Xu7P5ZdERKf4O=dM7rz^BZ2bsFD#bJK5%S3|*}Yu|fC zmUM;3;o~xzb4NGDno~Dl*S-Kq44%xF#y1t(&GX2 zii+80Tz213prdvnq+F!th>;;FV%!Xch{2?$nE;Rp&`^hRS@zq7+LBt%VB5)x0RVg- z5Q(P zJo@07nXyFf+32Ki!86>pGXS?G9EcS755zqKz~=uqp@@%54T~*~j3+JEl==P7Q4SCH zoZV4=%8JIqi@GTOtA@tJ_wuHaOC_c6PvuAP!i8aW#rgRu8Vi#u3lpigBqeq#Dw?++ z?U8SJ%MWpi%2O|ufcamxSgNfUGh9TLmgai*j9-O3C(U69})wts1?xO*y!_By7Lpe%O)3C)*3Ki+`q_a5x zQyKTnj3vN@9`Y?RSW!h1J3socgg!FGt}xRQ??3fnNM~_m;$N8pnRu`q@H2B1i9B3( z0CligBo3*}eSoNuVS)>jz2p}e#z?YsGO}d{vFM*$c#7g&QXk)0boteNvZT07dzKUe z7PxHXKV&>lqOVX zSS@&z#(L=Ysj;Lj@7>;;r*mt-WIr>#A}@PznWWq1T}J8&Z^J8_3q@g3>Gm@oJ$0gk z{F~3=$+HH^U3!)`!(Fr5acn$ud22YwFd?y1Z%%^Q>o2)^RoXf(GaD7V>04nFqUWbB zBjwsdLNC)}d(VWk5sHy-RPDW137Y(;%W1Kov2>^w>v=%g*(xz^zcY^sry%}4ZPxAV z3l$PiIU3Khofu;@hj^7t(q@wf{UyD) z@0zs}IUSv8h#rDT5wQM@8Y}fTREDw+M`24}qf3GbUFK@_@;8<2%p4)ik@ES?m*hkKOZk{Fzx+AzS-MsLFOb15+L;^UsIa#yo30=wQ zh0`cWKaCIjMN@z4^bP`a@{tMLx>%9=9;L0jTc`bBKZu5PJv%1$&OBk!8yOEVN9q%Z zGpI-^VeWzSz=VvdhN`86@lI7cH#V5doSk`k^-HC3I?`D?0(tL`>TIh2RV82dm&j8Z_*t$^u$> zWn3nu-7%)lE$`j;8u)q@5zH4H`vAn$5ZI4Zh4>#Kw$iKI%ERhfs0`08E=aFvJ#)Mr zJcvRMnv31EEG4HGpR*s;B!xVK)z+yk8lx<2+K%#Atiw@g=^9vh@Gbd#Ki~JmMxJF` z1V0#mqqXzRq~xJ7K~ggd*D`j;PmRE>sw;O!O*1Gn3<{glOt>E}aiAAT z1qm>Vq*hVa7mU05BObf{t%+v{4SX9h+wBwiv%cEC|K;0>JRXl6IN#kh&(q9)nrPqq zeP4H^iiM?8=etH*jeeOwy)Q!OaC2n0TeOk&KkX{3eLLR;Z@c>oBKa-`ngAh&>{2vb zJ|9x_f?{8*m=A=)dmd$8;g2ym0i~8fayt7CJ*6w&^+ANurquw&=xJ#d2H8k7%iEX%5I=8xkHT0kyY< zC;dl!Rsze%n;F8gn2~u!o8;zh=0>9qxE82B>K7O8@4bO2e5AaZq(bUII9f7fc&!M& zq{7>UDGRD*L7)V0^G!}cZ&xiw47f!YO79ZnAWN}O&wdyj#0my2U{#T9yRU>ca$J5u z9@U{KaSQY$0k44%OJ0bq{`S{y#iF)S^89c{ULbWKl}2Qng^E>Gb^~7A5f)wTPD%_+ zYZ4An@yoKnJQR9(e>cYzgCS>CC#3Z14U2o>* zE-`%Nm~YD_d9iuSIk}^|bNTr7*4Ca2|Fi3*@G6yiLrF5t-GT}=P`E3u4)-#N5EEjoV=uPEps@IRA;=hJHC>wjV= z8n>vNc0`Fij4-!_gN6p_J8(>LM67{#>)XY%^`MS4({g3FsBUyyMJ0C!#uR4A!lC_v zVR1sse=sylW3lB!YMRIIAe^gkRCR2`FhpgzkcAW7^^#Z|DNXenbJ;iqqy(^lx`B3_IrjpX4N-UfWlM1x6V z!^R=L&zUx0yqIW`1BOJXlpb5GROPlnLV1=&k&MCr7XR=;duF1gLt*#1rS&m(?@F_f zKVBTu_e8CBxr9UVDd;0uO;p8ks%2bjMF)y^=m1doOn-JTve}%y7?&wN@bMDE6QoYc zSR~7wQ%!&>rP!naiE}IzRn?1NTnPQmvJkEYkpt%{5{$s2OEeJ2L~O%jnZ~Pu&;gh} z@oin=HW0#J${$Rw;@H==YA!a0FwaNMX-Ei+yt?x$TJ~Wq;nb)PN0opfdI|Q(pz2uT zI6`f(EOts=G3s7n1&r#bRJiAqa`0OsvJf{!Ck7sYo?S#0%O2Q)JDqtC6w$AmB`Gg2 zOqLWS7UC|VazuYHUFxT=g!Yf2SdJG1o(q`kumDqCAvB`{7d|n3VD_0NUP}IKL5oVD zS!y|49WlGl0M!~WG4F@D>LVa`48li~19?1};vuO6ItcKg2hl2AjNITS(xGN4H(Xv@ z?%Rrg0Lr#hE&n|%hoS(T7ND6eWFidBIn`1b`PasK(C(Tg$w@E+%L4QfY9zoYR~g z4}PFon;wkXY(dPfMKiQMWX>#?)x60W4R!O^jpEvK(s36JMM0Ki=ZhYe;kWqi*u33i z{d<^6o6T=5+Ox~FTkF(MaZqq&n3G5Oj-GmFDt3@RtIKduQJjc;al(MGg`N8#&UiUO zClg8^UC}iDeNZW_+W6o1h4vD{VdmSIoGxX~kHlUqIJOn_7Z12{l%^nIPzW+<>}Rz+ zM^*B|%Ew?DY{g*E9^lQRS{R}pEv3CQ7U@_bL-I3F2qFh`Hw23?huXC{RYA`Y&DBY1b$hd0JcU-30wqCQ9 z^R>y?DU{uj(X)%tzNo{od)t}f9JWzgYQ2jf+7spTTAaLw7xpfJLuJ>_EgG?vHmC9| z#sV!7ZODLK8+BY0V<;KqljR_9gpKu|#Ca!-_{15#)m&U|&K3pB@eJMWymM`Gu!|RO z^IG$%8ei#(7OH-v`A=?jl~pIQ?kzK zj#`&IT0TExyj+J=Fz7NeNE+g_Kd=(Hu2|Q7z_+q@ua;`$CFDV^MCbV)t|`VJYEAw# z5}NaGjXxtIsd`}2*yzGc^=6J>(;dYwg#&jilP-J$RMB1CoHb8Tg`+NX7@`2?Q0a!g!^9?|&V2yms(pB>VLW$0Kze-2 zVG9rZ`%qkblm4p-_jEy?u}}k{N~?YF1Qx(XV?(z@C(!#zSO}DA>2ku;hD;lCPX_DA zGPx_hFb94SL$iFbucUS%hlYj28!*G+z0-1DthmNcpFU%){6rs=c=d~(XPNpKt@fj056HD9-HN5cZYEV z6T~25R-t~cmp70Pg63~FLgwY1H!ImhGT8^1HOVG0n8UzgIVbNjr@=6zOF&I5YVp>g zf;=&v*6In>wRWLiG_kGhnSqA~0AFCAC2K|}8=Tg%sE*^#UD{OW{7({Im3n==^6*OX zabe5+ueTtK8hxi^ch*>9jw^UYmBLg^AOtQ@+`fmT5N8;wja?t^24NUw2*Z^LPg(*? zn8h}${s=bOOqCDUh|(ge&~mf7{4*zq?}Z$$yHT4i4!fi?p2XFF5YbfWU7AGZ_^loH z${-v(3`x`GN7&p-kY`DUgpjGI!qDsD3O|JSQSKxl&cJ!u2IDUUOKSo3m6StT^vw9k zQj%f8$aKK~Ee^G&>^S3hM#PU;Rf~G>wyF4dGK{0iKPYXZnVTV~u;}70+hO>!AY%|xFh6H9zzL_l``6`VM^IY_FV+(iuD#YS^g4W@1{ zF2`j`hk;B&zd32@0-}?7t2`bS%LHQvCM{UL#3GbWa(7*EH6d!hbjQYj2WN4>4rtO~j4mHgsc@?`I63=^ zF>AYK$AAfC$FTa<6T?HNZ-dI#Mvg<1LOFxg$_T|^>9SjOb@@JN>**B4OVbBm?L}6p z50$$9C8ZVco@0)r#ILV}szO>SL^@KnNNG<;!Ttn7d4-h(QMh0S`5J-lr94t83A0R>(=2sy1!wF?G)rp%nV24B5OscAiaw3{rcortiwh?iNz zztE{!ZN1P;ORAs&_=F%tvG6KzO$f8u3b6asC4O!71}>FgWp=PmqA<^oKLykX3NbDSGomS+xO{45>pL9NYuj#p2HB(!>Jl zns$_nbU3(bVs)+p9SUK8H1x5)gY;_4oRa>wjyk%HpoP15`=THQ%YGXY&|bc?UE_qg zse9qZX*el+ES*WRzDddNnK6{_ zkfc437cNbpxMKYQ3L!TC3d)kFx~vr#%?H;PId-s14&E`E@{)QE8r!)Jfi8!Oa?Acy zGUJ$Dp-n=EM>_^%(rM~HtdW~n>A-O0b88+1^O&NmBFMuL6=8ni3$DgTDgb>$l}sE& zv}O&hH!8&=>bl<(Y4&AErb^Cfeqb30QR0 zxBFK%$HBtAprj{B0^UBRwyv|bH}*0_#W;1<+v$#@Ug*P+>z?;5alcU$m{Ci zUVUGR{m5k0g!iOZ(m?H|N&kf~A~(>;!g9T*9{W-C3ZuT-oefjhXT%a1lZ&ml{8`b9}PM?#rhw! z4%VVQJw3g>`A8w#w%nv_*d;Jq*Y9RJ_%2)lw;_lyurzRgU)467UMra}HOOw~kN(;y z+nu8KQDd;WJjVSB9B5|KGoT~K0)X|165#qL`E5ACCDZ1BT`py6@2#6=3;8o8RWWXj20ZeJ5jA}+`_(rhmFN0L^T{fm2z z`Uee)D)ZJ(TmLzTbLW}dVn=%nl$9!hrE}N*kAZ&rsZ{yaI}VP&7ZQnDyyFq_gGRqs zd@BF-J;(htKAwpS)+qgCHW+%Z9`XfkOhSSz$WM1)VE@?ZCsN;9b`yx?g5m{+)lyJ1 zk!lEBI)+P04Xf-5(QzA1qF9AFLG~+6a!X!Z&27FKRZ&9V&AgHTkwGiko3+>P&y~;y z`3o_f2pB^qRUI@1d>QO`gqPW>w%X2~rO9}kh@4{1yUoM{F+;`0j6BbH1>`hnAT^*= z6RBpQMeMEqLVcvg=;&x>&2M4ig)_F?8c6`dk^~p!+hp_Ht%=D>Cvr&Gjw`&OSF8Hx z-vS1`EOQIF>{n%ctnV5chpr;lnvzTgHw&`9P2$0iX5w518`w>uc8pY=+g&?PxuOg^ z`ae~suL=?L6I4%UR=C3m*sQ;NIE%6s<`Apw{gj=Jg;_TGMlGR-zh_49jg8~zd+D+O z;lT>Vl@y|6Xg%@dF-3YEm>K=iCQG#3s%pdJB~m$rP*HGS>fadT zR_)0S{8S1XCAqQ@4tPd|bT3{rO>ay-s?kq@7!(goGHqJ7wKJ5Jb<#vva+)7?ry996>E$BUFY>>o_&}%BEI(} zCWjzl;WHDD6^ns89Hb@Y#JK4RY%my*4%;@$%=zU0w#;25P zXAJYrtA4U%8zeQT?#8=H+cqw?D!V~Fs>7}OyysPK+_(*ah+*A&h2fl;7H6I@X0jH3 z9&Qkq4G3j0!<8Wwk5RK0R>d<_)ATue5nsfo2*=4NC7ngFCqoX*f`4PJjjBK}od_$& zD$tGzSv6cc;MKSAFUAUujIn!T;YSKve#sdYw4T;?ZLJGBmGC6`sxYGRm|lK0;5<*U zZt8N+dXnt2fMig3=ZgOzQbG1au;_ygCOcETGQhrj+s5iU-1~ZidB<|TeS`DYSvd?` zATrv;;uYO<#DF9$fV%I(IM1+F*f@1Qtdak|_p->|72ON7^iZy`KS!4JJ>s`{%k zYkKEsf~ckdfENES!zt_H(f#ax0P+hB#bgAB0+`k?*dTR=ipoyk=;3LzS6T?ZtC6Mg zrgQ$Y5eNvq4-Q89_v!!%;t%iS@F&GYsq}Wq z!p+LT*Z|b>seO*}am-8i##@8CPLzo>L$)@7RvOBQ&Pn}=dtQlqOUK;z|fVim2=K=={%|LH}PSo)6P!wvKv>z57lL=uB~mKd?ln5%Nnv%nH=47$O z4!26i=^Vo$^wfUAOzGMR(@N>Yk_p04BnzvErX{vq%Nlm;`53o5OPk#7dwaZmDrvPA zRh?SA_lMq$Yp*fH?hZ{uuuM{APG~NwA^==VC4u9Prw4Psg+P6 zX@_Qu6J#h9rl@@LCLO*QE@p%o!8PorcJ3mV18L@oO7h@;y8i#`=IUmOTAT@ekY3i#rb^)5 zW_Z#9m-lxu?URwU4R+0i2qbH|KwtK{e|hD<3y^Xt9$0XR_aKO=5~E={k07~@ZTO3yO;V zX?%K8wzwMC&OY#B`4`o+%nMVWy7Xq-^h@ue;-_shk4f`O-N;B!OH6?2J0#@@9pCdh znU{7f#zAP5POivp!NsIw4tQ_((|6ni zcflfnf9x5@TP_>|!1QdvA&)U-QK#lc!k>wC@Db)=D1EF;U~j7BiL}gjyE=*5ObB4Y zs#3--|9x?K>H!(RVX^bbz)j9QLU}B8yq0P~-7V-R8S$d3Q6????~TNKm?KQ$-(BQ0 zzZCOE7U~#~F6nl-lUrmCIKjulFJ74VkxkY#*u_H5OVYdf48sf)X7#c{${U13J}^xt>CwDzncZzBG}F+-9lg?fN_wE;|8qIv z-f@&mkq*I;?65!|>THEwPe(P99}iKA{n#-P0Dr!P}(IoGA>@R#K()~Ta0f;3!^!8#SL*Wu^kl(4W+A8OZRG7#M z&LGG*1UoQhi(H_!wZvwq8L`FFAV=J5$J>S?Ny^k#)eRa+^mx&#z}usPNTA7mPkO?? zjH^(?0fZd6TsX}rP{|}f6P@infR4ezsQb_-lLL7JS87(EJYgeTen61yJXf#?`IVTO zSP-Sculg@Eu~Y=G50N#wyCwfHy?Xb%HmtZP_vGO{DibYX?XcyLhkg3>GM`HpOp+F8 zNo77TU@=X!XRhG%>-SwS7#Uc!YFQwKZQ@A>AjrWnNEUDvIA-!o1-F>5Hk4%OcTZH@_B@2z2oHow~T>e!D$vH22~ZAaH=yFO~< zvC91$4#u@LiO0z568W&kHu~wDY-prU{+Oy5-z|d)UgSkKdLRKGX@t?;Bo`Fs8niA$ zKK?ln41|23Nt8PC4S{H&=!~Q7E8O@jere0&9ofEk%^N1FZ>YTSZ9;2PK9 zJSyXZ+!dWW>|u;nz3a|Dc<~AOOO_`io^1X zIEI1!yFuEq1&?vh&o17oQ}xbi^h5xug`V`-J>K~`PttVdPjXypHTQl@*Zxz_hcujh zp{<~w!E(FWX2M(ZMTD&t1?nd$KMb1GS!J5A*#I=||-6s*=N zm#e>js-!U(^M9j82^uN@0Yief3QX}AT^xU6AGtP^{DjiQH+uN${ubo8j;dD|i~dg0 zu1fSI=0&(m(AWf2205fqs^^mBXcH0e56xkVrK4s6zZkz*Gumjr5NJ2%b?eL3bY3?t zGBg;qD)ljnFF*fKPxiDiYD|1%gM2BsdH+b6iz0X5=g(bH*>rzv3eh+@=8c3mpun6~ zJ-vDY#W659%{i(fKbXCFL_%1wQd^ssDS3n!!wG!X;LT^x!dx8#jE(KtDK-`EkRDO6 znKw%Mw&VS&@A#@K3ezH~$<-y7GmJ<2ftu#yvqZy!7rx!>vm0CkMBGntGYPO*%RcdL`?Wrzh*fiuA z%Z~#~EnK$8Xzj`PxetJ-pm^!%yvilxwjo+`pPL@Y9*LLZ=mUcw*xM&FB1Y6 z@BkRUKK@XLYRZ_vusmZfBn1qB;gM70DEARCC~Q-1f$IP6%jZ|DjynY-%{ zT>lU!cqMDGSY4hKEz#fX?NtizydLK2^yC^ZQ!Z#O{5^im(=qk66{*PLYN3|BXBPtA z06nxF9Cl{FZh%dGKG7|H#q#dU#a#4k`3+#JR2Lp!I_{kJP?nr5gWCir$`qn$ znjV%h5)_yY=#z;VB&ASU8x5|(%?z@&Y4mW0h{5V`KmnQ5gfB7qh|y=B>HV5p4lb_A zJpLE~>Ek2`2i%+N9D1WO-@fkQl_pC-hKh0Rw)rJwSwzs8L#2z6GO0&aJ2GN%r9XvP z#ZBMNICMLu3^lwGZF!-r-;Ni*j1MCWHw8tcgg99WK&ijfPmrr%Dsv? zE|(Ue|LDzmvg7jiSsSMLL7g$fYT63{A7;?7Lq{na^6dAb5E9?x#rdU>PTGd4m0a@PbygISp2 zFJRFl&wH{uI11+6y|TT8rZ(ICj9EFb@6oe6w#a!t*kg1Ga=sUZs{T=hWKJLIZ9~9o z(uLk$L0T&Vxy}h*rfc~dMd{XZ)TEO1%I+i7Yjyl_z50{WVz~o}|2fzaylk?|8lHi! z7!{&O7dT>mH1M=AP@gLS`Iw~jsCP(`yh`1nxFQzW(jLav>0s-j0AY;GXzCAeL+{igCAg91$bRHLL7V^UrHj8n13$y!)r;W5VUK;eQ5`>P?Nd zY92kAs{-mU?;+`AH>xekPN^=5_FkZ*pF}^Yjv$q$`80{HK}w^8$N)^%XfZoUxS)sf zeON6ghhhQ4hW~=tkMQ}XW=_g|A&SD`Zf9=}8w0(WWJ6PcN2#tG!w6;XCr44Ww?|l6 zC4N*T*-Z<8bO-VG{6~Cq^3?vl*W#y>+L+-0mFG(k5#iJo48xdj7O* zq}fL*ZguU>=kq*td}RpId{rEAs81!VcB?9@lKAhgQWXDZ=}_aq4wrZXZO48(ejm}( zNVVS9!PQ;+t9DqVoU8qY7Pi2rmfn5t zl+z?7x%3+mfAgYCLp~Xe?++p&-pDi;ZQEivxG;B~qZjX$Se--u0GRB1+3s)Mbt-L| zCY#A<$U6TM}AqYO0L7#t& zoR+zI+n}Q9{rgm^Uh0OJx_ax%W&B7!JQjTOQn}U#kA^b=mg=3y_P(M$ty>L@yd6>~ z5tO7kxAuG<+MMQ+u!^v0Q^;Y<>b^eu)o(#|>@*;`%>tP4CR#T#^|^d`9)# z{h2M5%G3VKEY8MmmDl1B!8~Gxt8&$-0*N_-b1~H81Gu+FS28duh`!nT{+Wh{Vt+6I zh3^ui!irW#x2y+eE8Z0oIZfGIyy?~d7D1W7d>?0=zS=DJ@uv8A7fsL9?z?sGRo-p+ zrGr&QGH292{Yj*DhF`?**S_zEEe6`e@c(T1|D}5T*1<(vZj@Be|495ITz?afp>P~Q z_T~!)4F9-dA%B(8uLgKYy+5X{31{f81MK_tPch{AQRx_=nB2jlKfa3W1S87xV}laU zmjKM!DR0VR4Nv;~^p2xD;!(G_*2ltTtszsZt75LDUic4gFgOFtLClQA+*m&;RI%Ah zE8F*Lhq-;@ojF2qFsxD7ztu*!&q~)W|TfeVHhCb)Q0jPm zUtGeh^1V8H`}KRO$KUsBZQVf5)&9%Z*$>fH%Hp%SP$=wKnW}_4AJ&hsb!o}b#(QW+=JJKXu+vhn^vkkk-C#^1xW@2~cV zD|PSJWPn7^i&G8uuZsaNTVc_cKlp{qPG=opsC)DE4b++e_!%8|RJ(2g<-C@Xn~bQA zV0pb*o!XI)0-lu}mccrIg&EhP{|#|ktchd)`#$Wa0w5P$N3X1q&%>?QqbO8-(kIyk zx3L+B$SZDnzQY+$As6>?;9_{{BD9((YE~LPZ$9`l(E=0*rf0XA6XZWN-`miqIkU$w zNz2MUFhQV3+s7%)m+xoWFvGIn-%sdmkc`$$8~(hhwEQ{E6Br*f@<7V=X+z`4X{8UiQAPJ9|;I%AFK5f5{KxJ6*fBPo|XV z5dQ)&+`JEk9o|hb7_L6lkJLM?hL5y=WZ*gD*s#VTRY6w;>H3LhN2jciPy2(h?w^r~ zWT)BF4`AUO#nNQ~2r5HLD2-6`o5udy1A6ysCz~)b5WGEKW<(lWJ816soYbYDqwl_J z*=U9_FYUmxiB=M_2$)@;_i9$IrYj^XOn;>gBbL}7L{_g@ZX-hy%Xn(xu z7z{#$Vgm|p$O?&y61Cm_dk5h^mbkK;u)(na4tC*I;)k65G##Wy*UywhFKuG{7dNpQ z^6-4LSj>H}7XbcKBv^+1qB~mvKX1{HrE0)4cMy-*U>kAq6ZbtI@Ib1Pc9^r4)%^nkc@l6uXTM{U)oAmD!L_5x`aNpTEENHU`dFV zEoY&yRSeUz1=V(9xiM?pmhx>uQs3BpIcx9QR~G2(Kv!y zV&&w_+Jaa#|1R?llhN}#wI5WySm_RidmS#P)DGdP;{v0VSz8$k-WTUjlvYKoFn}fq z>d^<{AB24XN9WEn7n7j(gOHpGu9`@pp)TB}z?l2}Ma_1Sybx&gvi&bIrd=K}fT@(* ze%vegH9n!?IW94gd)o5yE;zdSWNl6kbRnq{bt~k*Hyqt({W)iErQchRu+wKWkn|nM z0O$Yfe7Zgx{;*Ua|li#CF^fxABdS6!3yOPviDJA|%j;*?AUJ!gd)T*(f zCXA#q^%sX_&S*9nD}>F!GGDd)IWLLz6*|K1Zjk~is+v68z3OS$xLG+Db-CqwaLDtT z*}oFDp#|<{v{VS0QDtLjB|8PF@p~UDDpTSf-^l7jJGjZP;@>?=iU$ULXL)VU?7#c$ zA2YiNw#)G(ei|!M@W@Yh5XF@hrh9*Q-rCUMDXw2+MDL}JIBBKa`AwKjx!)2LWWd>5 zy?XGyaz^-Q>)X_SPKg~x9^UCGOj`j@vk+hIUQR~wKZch-vlVKVsk*Mb6xvS*x27;i z7mEbwmGT3$@?$yUQZA0 z$hlo=#7Y<(f%CuWqWWJ;>HL@fZ5?|i)ZI!dP3?+qjd&Hby?oO1X&xxX$EDELsMXYn zuU1ug&2s+8A2j7r`xgLIs8IRSo1{;2roVe>?<|j6Q8J3-T(@7ikG5@8y@L6p87XgCSkNTp!AuMnh`FS-a zSbw6c6JABS1sdIO1v)yO3-c%#Zu_86oWQjW9aDdi*z1SegYBNwCnkme-gI~|A{e{% z5LoaB`Z__*`W>mUM|?*8(0u;03Gb2ys2<6SHEy2)JOsH)91x2!CdL7f-F*EbYyDvJ zjp%WSt+3@(b2lB{l{OSj6ZI%K9Qj~XUU2#&Y%-QOY&fh0;n`lMNh$$SlHPi-Qc0G2g#V`r z*CQ0ehX;wai`4Cl;?hQ!Y7uB=ybM+AFUeO_{%;UV*x;Z?O%2N&Q$--9MIfa)_K{0* zB86L5g1AqPGmdx|oj6$**p3y*p#sbR;HgGHMLQB=SmPDxsiwh3bhF|0M*8HNi5kRb z=E*#5c6s5k2Qubr)-WDfA+aUN6{)%>IN!`E=vjQ(ykLvXy&C7ial%z208Xv7l?JW%+b;f>XzG7fRqnNj$j9aMf!Al>fRAE%E8W~O z0I{^?6SmJRtFy^Cg8{|o5}6uSaLkI30#@-BYmOU3yFlwweZfedR}eCeEH#7oS=&YoQWGNU#lO%cQcF(k|FVUEOMRKT3q4sV z^zx+L*9FG|<3yC(#kF@(Elyz@<_JJ)=Qm#5jNlF>1i9}2j9 z^@!-ay7co!d7wnn@A-NOJN{MO&OZU1B0eK#*OO+sWy&3skfANW3gxWB?$ z8LLpFqh8vkQFI?D3&0c18CM=N&WVyTtW^ta_V``58+Da{ev)=QbAq?)$3FWgLBFzf zD&E3Vp!`*h8Rc|)8X`RTF)prvu%E*(m(FS2mg?{nyMG1%KyKgu>3s}e zIKZfD!01=PekE790!0L>NxiyN8Xh(I#Poy;JS?m>tUb-M5TWEK*k}aDd?mTo$oA2;-|q zn8A`7m@mHbryMQSaoW{>Zl=TYr$~S_fsw03L=EgG6b$qC@R6Vo&L+NH+(&Zi81GvW z7N==ZdW%~2Cs0t(qM^9!pxKH?NxlpH+CJ7N)!C2V-u6WqXsI+mkBlB_{yWTkPoqEi zng3fut3>JxH(CvYj<8O2fqzwbYc~$bt)}(U6~&emUq$S*ue%W#o+**}%%wKX zp}L{b2KV5Hfnrv~o?d^GRf*NG+1$f@Rbmznds+y?Xe!y&^S&x(MkP_L9-cr!sylze z^tXpJwAHh!{2r}@0Is$rK1mqTip9Bn8p8K{{)gGc^O(O2{)PKvXBX2UFW#f0`#0c0o_Uu+GO5df2CGqb|A=i;3vpZ){Sjkzctp+tZMwvpP!GkxwIz?ys zNN%x%E-j){VZGx;$x#YM)Kf{#v{cQCs; ze|@kJTyyy_C#NyHUwQpNk4jDPGJAa^bgIi-+s7Dfp5{TuZqAEg@Is%GlZVj9C{QU! zC@Rfx9Lr|mr1wStTlJKDMMJ{s2ykNFs9vO~fucr6Zq64F^`k-RA8nF^Db5Matrlqb zH}YE{6)nDl6bxGh(jAq8h1PfY27Q#R<5gT)|Fo+9k8L=uHDkB zz6!)BmsZZwMSF#U*8hS|lQglm3g7+NALj2qPJ56?(ez#Up^dGYIf=4V_QC3y3XQh> zDD^u~QQczu1c#G2X4o)u5|jYIBkK}_0MH~vme&$Av`QO(*)=Dtjkg?X_xOCOiSS(w z1broS;*B-h3Xs32LKBT`o!G6X@6MD#2%dICc>j2lan81M&)ICuxGS+}GRD`oTbNMO z@{4wX3)UJ8f`P*oc$NSVYdiWiPD4wXlU@d87|J)=`Iu@nOAi1Uv%BJ!8<~I$U@;hp z13)jC_r=N0I=>bXj*7e5oVKo}@T*{Q`{bie5u%-XcTSaEA*&7*5TDz9yh~v7hH4X` z-MrqK6r2|gI}f?6yzWMwMdj*W<>o6B8cS}Ox+=UXsHs~B4D@b%Tx&mb zrP-b6|Ec#gSMfXWI5_aBHs`SJAyu334q6b8HkMkbaK@7ip2g3JQZ?(Y-sR%Gv8oTt zO5p`y$U=O*$~X^b)sqWebHB+q@WmwFCo-dg#i=2T_=Wx9AVmmz2q*R@$zWHg8gfWc zJi57<6(X!^I0*`(gd~Q1=`$djzF|p5h=_(5f)A3w{x8ntS;$SA7LUk{7_5$q9v&VZ z0+OW#{eJ*|K!Cr(0XQ2Q8yg!FVA#eDjALLz5GV|x7(xRAicl#QG*Xm^%$TM5)f{Wl zm=?nu4M9^-2$iNZh*1(M6d*DP!T}hiO0K^c{{SPKt<$7Zz zThvQEDTtp=XFi-{C!rrSFwfki^ur3HjxP_zLOkW|7rq(Qw3ucHbGHHP_R;no-hRfM52{Jx5)Cf{i$UV$lU~JiB%C4x8(9bnCx+r z)@t2yHYKJ?e_Kl>i%S_>R1nXC5T9kb#)tHMrayrzhZ99|xmB>*vLi$~W6>hvZ1T1!cbi9ly8>z?GM~ifPp{27$!ED~4{|gQ5_|5;@- ze~|22n=m~H*V1`2paoGjVEiTGtr|eH&4=2%X6`dazHtRJmqO*2aoJMFsm|2%VD3WS zG$g$84B)+cJd;)xcrA)W$2G`Qw?~F~!os=jA8D8{&-NNkKFf!inu_tyn(FXm@-wX1 zkMQxZ;&%G=zAPhN6|dicMZ`}_rNRILhmujW@Tntr`z-Xe&0XcJUXM5FDXIEGZ>P}B zjUr#7zI`DS{CIf(^~ZJ*(Y&>ZLWm+D0R~_I1Ygg#r7zfn!R;>vit9YR?i}g7Z@_!9 zL2)_+fCzvf)1|JATvqLAnIhpC=&^pt9>fp;_QPGBSL%QPtlR(*8#a*)hM((G3K?~7 z;|g0y?9<=!`&T%)fDj^0J5_V#>Y3A>TAn%25#_Em+Mx75(l(uQ9#N#S_FFpt#josd0icBH%v^r#`3vnG;;{0grF*o;Vj|!O`W)pK0q#PiufFM9- z5}bF)7t%sUtwkEG^Vlf6I=33KfnHG_ucT@r?|Eq8$`b;I+rcKF^KfZZN0+6lx76b{RQJAD ziERHpz3k7bQ=hANz~ga{{U8t`C-4|fcv5Vi@PxtMMfH?X)uDtyhk12wb157%`vtCB z)Wg2^iq?MHi6JS&;dbm^Gk9l2{Hyx^WS_sLAVyA;qU!VZr00rAsBfrkxOyP$BOTIyi;s4y9+1};>!CFSGT`_0FKR>C)^IF^;Tc0pff-W#3_j2-5P|_B=4(ih2 z0x}E=XuhGk4?TG+Ni*VY*n04fs#sy5I@X@(+HQ{V2^uWu&3mNU6J~Lj6cGn0D}GA; zTW8T5vi(v&fQW$*HT2aqbWeXj+u2x<6~0+nj0DX6#1dxnuhFp4#9yqot*Y(4C%Z|5 zmf3!qKW2asDk;unRp;M%y6{IqpzR|S%-I!u)-Tps+@bm%tYDj{ys6;=berag?fCaZ> zix(K3=|rHl&fE3a_Vf%J-bZabTcPjp;@`U7*T#!Z7bT1JrnQ8&p38BF-$xD24&OfB zZ`f6}cf+oszFL;`?cA$hx^TV(rX=~cY>fB!yZ;|)w}@|ZSjLNvEOp;UPol%>4VE|S7QlOo!JB`&vTgp~!ojFYZC9z4(c(A1G1y#v<& zb7sFWz?-Apy*{G$2xs~Xu`$rM7Bcg8vjhZ?of8i60)&(1iRo7PM}hWJy zSql_^0S$I#r2@1za!!3lT8*%7xPEH3jKY8fK#}U`ufYa|uM>g&I=^aSg+&)}`g)Cd zF}(tJ6(`x;D4%xS6aeA99uuqHECQU&R0Q9-eCGwY*g$-XI6br0?|-lO+l5neAgX4e zIth)cu|wLR?4GI$fzp@7P<{t)E%P@f-~fDJ5fQ+}+*>QmdVlBCCG!~a7J#`*6_9#P zdH$+WP3)?vHL(q+Dx6Q_k!9zBtv!RGJuQt#YG}2W374`fg4-jAy$i=IXH9pV7VTH9Mao! zBQZ}7sk*C^1PKYPJoa#e`@1C$&W*r_fzfeow&SW&BTbLX+p$S8N)l3(H7@@<>w^ch zaT);nyFK3r&pk_My)&bXx%u@YW=@8W30VPe73)CZQA@O5`n@viX~<|;vGe@xa$M7}5NKv)Z`8k_#7NtJ2Vjqvq&mX&_wV((&@p>YXsu*lsebLDaB|q4TO_9gBn7%x15s={Oipq+HN+uGWCeQJbYfEXBsQATt#%cwM`<{eqsQ+6QP zS)HCXJUef7`l9gLs+f_m^6YN=OrMVfHnxJ=SINgGC|>iOW>+Oqid6F9^_Sh79T4xQ zFz{n}Ym_w!ns&ve8>mXim7;0@h_3`dg69rf1Ve2X?5OOT#o5adnfr)+G_NKt@gK!M zx`AFJvtOR3u3DGg1NY1mg`8Y13A8X-8>i{!Xvor(3pJ^g1T zu*-kC_=Jrul5aYc_GnOhh2Apd2Zm`bHj858X-gjJp1_iZ?02kUujCPH}I}sZ__7B%$ZSjutv=1V}E8EHetw>tYR{KSuBo0yLIH^ ze_&H*I>a1I)zY?XSzr{I+_@YYW|b|<_R%lbrN9CnMxB#^q0YF7ahN&sw)$!I=kRH& zG@oSdde#z^Mt56VzaR*900dmIl0*ZN{j;f^*F~~u_G)@bwIoch^78Sb4)?X)T=2gn z?l1WN)~fbi3a`>W#Qjd+>o4k6+O&VI_4%doNxTqRX3G(y8Otqs$$U+S&HiphV4Uc= zjL~gSw8e1ML14ilD%XI{Fcv7ujj$6GVA`-5y~&Gg7_z+Df{%EU2sC@O1QM~TP(+h6 z8z|rq3u2AV2sNNh@d?EnT-i}<$}P3&lCoP=YNFX@iqxBZb)w2-(AZ+H zrEII}(N?k^hd9QNYT3|YF5zjK%PP${mwi-Ctf-)Kh{~sIMes!0G*sI8 zyPHNJ)K+yVxy;oQd|I?sl&y@zn5}J`?Y4CBjAIzv^TQ_R1t8rt0kG&|L1DyNsD!mq zAu3QuALng8_b~{`Zo$FbS5>Op1i}vk-t&UhbprzCnef0(6yJ1XnD*_~_s3WG`Q^lo zMg3Vqj~%_}F_BKCb z$?>122$>u?bJE@6IlImcSFWEo=8jb?XxQ1wQ1Oy^vunGi8|)i*S)uXN0mQ!njRo9| z2LaGvFc=IFhG&~?0OmYiOVt9fPK&tjX>c`*tot_=i|s#sFnG_W`EnbLfB=Cx=w|mk zI!L24UTyAT1CQa>u~isUER!VGy=A6b!S_% z!dkNKs8n1}p)a2xx{w3`76<6UF{e^()6u4tZ`JD{8ogOoZ6}#<064{;#**cXJX(Y1;Dri4tUb zkg(OduD_n}OgmV9$AU_+6?qD!M4%!PmY;kI)ra}H!vHEx-5kx7k!OC`mvdP$8mPp)q6{m%r{YM&otPPox1Vvv`6QiD1J5JXTIs z5vSfy7}w0)=)!NG)-3b}S!nmwxc*$t;So-PAzDNU3=>k8B6RmS7sjv6e>?O)IqUs@ zo#uIeMXGnLUmDK6*SE&yj2mvNZOkWa@X0fL-V!)Z+TGw00whg3+dVS7Ui*AW+}bg@7T&Zy#G4hdR7M}I>CKKjFYWvTGn%mTGq9us;%zl;Ff8}@FD?t3W>BT2l@ng z{C2t`=sqof_-ffNb5Y=U`Oe#r$Yp!Y6}QFxTvu7Wejzdmk_=Pu)P*4l3{)bt3N(@s zkxBzVq$G&ZN(CShlp`TUAtYEy2@(=QMD3~?ilGH$0ECiB1%eTfAr=u~77#`dA1a}$ zFpP|WAV|naNJvOB5Km%_3dT|r5)u*;5)u$0Nd!a4Yq+!lvL!M1Q^PAJ>y@d?AHMZ7lb5COi|OPTq0sp zpg@4gqvy|_Ygq)3rvxOJLOnm21b|y@w%cxebLWg>8rV61L7*WdRRD;hH?*jT3NTE* zGR0v+=I$~>9cUK1934uH{X~VRZxk*Cc5*7v6T&ZWBd;!g8OG>FM1S7F9^d*W{-C_#xad@1M!$16W1!`ym6Y;?_5_H?f}GRTD~>UH`^G-HEUYd&FyPd>aD50 z45A_gL_mhv>aS}>t!lQ6v9XL}8yf={#xIMPwO|RGRS2q@qH3xlFu@wNuY#=gtrdM$ zRa(~8t)m*wG*$IgR<*2^>u~;eF#_Zvg9MMu@Bzy`E(jSW0V0xw&>|ur1UFD0{@d6h zo1Uy)of-@qWk=e;KAT=$?Q8~)a`P^JfYW%6{u_K3IPj#c(?cCg%#W6p%4Cyfn|5<( z3qhA9?^{IgBOX5~e)z8dBHXcFrf+S|-0428dtCWEKX;xW}+j`wP7@fnK6I12#6E*FYDwPhnxn1 zg9k$hYa@-FL!opw7@igqz%WISQKICZ_Is~s_-T9J^?!%#eL>m02epHf)0_$7*-XT#s+lrp7KN4_;7G;d`&n3iEbQdW(`AxB!LjYAWUMkl1f057gEK}GdaO0tS?)izy5ds zpG(L6--tgx-oM~}*JbdQAUM$yLr{qzgg*nSF*a8nrCu#+{co#`U8}ZZd90Tqgu6It zrUO#Rl14Wv!-i^M$q?Z*+4>IblylQPD?EtMc*Kpdsfdb==&L9ywSiSyq&80ZJmGk} zrvRdUcNURwT_w_ArUM!I`gT_Z!d#bYt_nI8GE6}gFiV*y#nL-1Vs0_a!J1L{HnBxa zlecf>Xr_|~Y)rAnDkaeoRYg@*RLs*wMOW-U4;aPoYC(+r*m{IC(1qiS1d143b_hUG z)hdv}u*O4M*1^Do!sE?gw57eSA~!tUp_Zz%QOxBqd@u%(fU;r`iD&@0fJjXvUqXGn zZLPD^t)lt_n3y}UMesU=;=ig|UB~Evc4H%)soBu-!rcipw0jpv8PgW6_|90tkoz zIU4$GY`n8X3Lg3)E&+3hTZ0-Y9h{R2n$t3ODMF=edq}Y{-}}W{qy}V{7=B_|1v)?l7;=BNRX5&VKEl8 z0Se^8swJ5jd(r>g_5B4s-aDhacu z-^F@e*Js*$MAZzkadIJbN6!DtxmiL)9+H{+^)}TjPFa0Uf37UYon?3M{bKyk{Wt z4-Y*r3~Ra8-aNt4cv!ThUKsfC1^h@ykK`TZNjI9VHBI*&2|Rb>9f}nTYE#*_DVXj) zu=al^<#VmmF^0`4(+@Xgfs#z3v)R*8_CA8JX-HPr>; z@Z&QRlO)wyM0t@ON#Z<5t;cr+Wp=c;W>9gL^nKfV%Bat z9S(lG)nwr3(s_8f#-?!*2Q#sX#I19;K?T&tI-Y6)BDTz+M(jwXQg^>LltMzpyutsY;D*DCE2>DN}*aHSObsWq}w0_GlBWhZAjkt>%VQZ$#0Q2>yTwbEt+(SvN@JhR2>Irg2M@Q2rJP+Hxf zofl}k?tSbsUoejX=vu9?E~Q=evm^0>uP=(SZVXDgfkL8_iC+w-qWH=bC}vVZc~)4s zd@qC=x%fW@2GqGA`1}weH`f3I?!5eaaU)_DF4C{}M9buN{^Ofu)7;v8E;JMQre@EC zN?wI-ne1DesuzNLf7e8zhK6m8pIlvFSRO=eL(plE$I*9oukdodPOCY6{{J@J%U)pg`Mb^Q zMc??gzzy_@5(xq#p(K%lpn-R#=d+*6;J8E|7smfOt&)v>SOCh|HA#fi#C6F>LJA9)m3Lw;}FZtVoTo4j7&S#uNN|Tekmmt$7|_d&Iie z;m2uifu%I68k&f}>Xe-Ht32N7mvlf;B2XS+&#TS9SIqW5pXKj^1DHu;HrTia-vECQxvV80Q@Z7-zPuveiXc3w6mvn#x3-RILz1xL zMP}n@gTfj*(&uszYGW?e=|ZdstAgC`4l5`iXdy#5@3Z3OZ`r>bv{BaF&~xN|ABVM1 zXWa45)b_to0E%F#a=Bs3A3{sdS;uC>*}T55stp_8vx2FBnjd5&5YWb>9C||5&gF@} zL1|*KWdiBTQmt|U9NwUVH^CgxQa3lij*Dv+X#@;asOIOCleOX@6O9+Og4U0x^KG~7 zdGg0C*Ie`jpMF1O*0bFh658mzP?ppbTP5ADSiJhdO8B9#bWV(*p_SjW`g{Ik_+}GD zveE*&xnz7x65-0^dpfh(Tqn{eKa1NiKeHJE3<^~{dqKt}b46Vvey+C7E~e6s`=9`? zX#pUz3SDvp7t?nf?vMZ+umFaQcbmtBi#yhR9H94oFUL**&_IMX^@+z*9l!dN^}+n) z_rmkKmznoI{3-4ac5W8ubw{~Oz!4I^?rOFlKZ4QCuslQDUp9>T9nPy?Nf$iN-B7>d`P@aKug~8d;@4>VSOM*Q(Zr?XHVv@Yu|_LMmy~y60N$aLO|56wDF-7H!#V$!fNHq(|m zJ0G6EFYmp#0^CQR(ZH88^>NvZwr=fQ0Y8k?VJ_QEUub}4}sJxOQqEFM%k_A z0Ems}g$@$86*0*|c3;Z;9$LOK_SojRJ5GCT%^FK#gC+xM1MUDNRG@$}>RI5Mb$ya{ zi(gsxA4|tnyRzBZ&_uI-*Ifta2)uZXp?3qg)v{%^g1^n0N&wuMSa-f8x|mWu4l3G@bM!Dk3O#Lxekwb7?cCwe z503YDyO#cT?HJb6&(Bv3yw%rA%qXYEAzNfQA7yn+W&Z#2d3moVf%VNu1Q#`d!-U2aRj zBYu30D;K&4o{Z^(3NR$AlgJ_JHU`h+@5|a!1KfO|K?edzMVLMA0l?K+ zPZ-7B`;2NUd_}6_M&gDFu?9tQ%NL z)PCFocPob7n6_FVdT>I%wvhmhUGk~pOItX^dvS=qEg_&o0UL+}F&W(BI4qdZ0N~qt z{L3QpZ6>5iH@|ELd$nS3v_~W=zuJtCApi*-$5+!=17PrqIezDv$(XU3I`@Qk3eT5I zo?qGh7U$CefOpY5Y`KyE4x*LKyi48YZd)gM(fI&~MBu%4cD1)=S3sKNH@+r&CxZLP zes)yiakcDiIWH&P&^u`O3Xr0gNUR)y^q67nQ-eRh)A!p$ZRxn)*BVu|=lmi`Di~WQ zlvKah?iMvJ8+CAVr8BtrcKZq?-h9`UtQD?y(k53;m(EftA|OGH*IOMVq|9#mVUdt{ z*Eik+k(uDF-1?bLu;&q_sJtzva!=A!CphF;S3Xs>t{mSb7L&cIDRuj4X9r6Uf?35! z9|6P=L@+vv`6}dYuRX+MT7=qV_GFSbosYCP>lFk1x@*<+d3=l_1S?{AvnYpGk-ofN z&243^JHwdEtn%dRaB*7CF9z!hv@HC`FYLhNJ=1=n(>nlTxBw%YY2$k)VYC_+*gP1i z9=)mpvcE1m;;vRpl8dkahyt9g(AQ3ULSM=MDY&%6+9n{eQE$7%OL2@%+bbAzRxLQf z5(L85h+%fyk+NbTw#!7!Ar0FUeDS9a9XLl%1Bb!Wp#jXiFLoo`L_imny3Ko!S%CI; zDer~aqLpRq(xL(c43z8-0sh~rvC-&$Y!PbnQL@qu4uD~ATcA#(dL2Q~0mVmlcSVzXm{ANqo-&i> z%7$p-!&{2*(IhB*xp8ko5+F0bIx_qF3T~I!lQF9g1*|x$F(r2RUiJaWf>*0$XWi3+7h~$X$MPCW?bP z-9r7Ll|10q;&tf)bx0L71Q1V&mQXB190^e@Z|WoF9;Y!7>shD7Ewj{T>0MhbZ))I( zT6$Kg$!?eFol^JJ;v9N8X-OZdgt{eE&)+_SgvdcXl8zE1 zZ5iU6tC~P;GUr&AXYzCafEfG41MkL-5oq*)09R3ZKUGN}JO88Zy!t(to$dV-_Y*Fn zKW6O(N4A%HI9|}jnNZzc*P`(VD-bIf)p9*m>o{qn;7+cg)5L33-Krcj;p->f z$8C){Lgd~=P+{V&IV))q?6wFCS1V+O`#^c5JEQmj^5ZkU%SPnu4)eMlP0+kAdul% zn_;AQ&e~buU2U$I++KJWZrmL#=7lHh&tK4R%kRI&_xQE1Q}gD|XX55Q%%AGsH(Y(4 zS111Q{I~h9!?LKLoS7gDh@gr-;6f0DAqYYcR6rm+Ue!0Nr6&LYt76?<9G%S_6)q?(Uu_P=TQxcp!4z1zV-A$~0Xk=yu}DstW6@3eVn z$hvM(_~HLtG`YPe?ro7lKrISjxGBImY~V8qtW#ffrnN||P?eZv4yz~SZ<>6VW3*N{ zwY|N`R@k|%Qo60xkJjSawgRy+?{>rP-EaG28$kpKjEmGk1-PP{rs5(7$QTh35fF&F z&wf#|wdLOz1{tvbTq6KohRm`?dG3#);8#`=F_};u1^RVLqM6T-LbPX{frBFojyBM; zkXbgjObwDchNPyj?fPE=G6UA#L1~!@C9Ra73zUoQ%H^=1?(BVLx4sAv&bN+p{!Oa7 z6Uuh!wQ-Q*fZ!!dP9JF=g(4_7-i@YM4N1)34XG5c!zf8WBWXkkXq!Ys&l{9jwf+~3 z`}uOl^*lC>peP;~4a z)YEahW|p*T2EIe+|6Ys#nJ1Iqn6QCInohU8ZIPZ;6M5EHWDH-JfIR^{UUQRN{0BVC$YvJV1pQ>#%rKQ{hshyVc)k|ar8 z?u=CsAVfrqkFm!3+hi~yjX;tUBBE9t;m|rT8ly0Q!(>8?3CbzkwNgB*4No4KX*0BA zCyr6pJ-^plQi<*k`p<_~(}jAiVzGG0tUZ0ca(FCAe+~HBTTcHtVUR$1dW-(bh_UMz4YezxF6K%e-rw-u3cAzy)eyObN| z4hd4dTDMrWO%+O3cJ(e&=HMH(g{iOg|8Mx(>gWF$82@A4{+H9-ksV~@L;+XaI%I~c z2V8zxwWZZUcSOfeHn(2QSTI&_5TK2D3l;MXACGGt0Y+w7R(prqlQc=T9S?j=YPEFp<3GTtj(xPui<9ZaYiF)UI?I^&S%$EiVYrx&E^2nu(ROcj7P=A;A9>c z9KkR_5Vpo92*cGM`T_#b>9-CcsBMr4aS;*LHe zHq2&hub4(-?6Ke48%8 z{6C3XgY?ZtHW)-i8~50H4%GHjxo?Se0EV zH4!paAQ2A2R3po-0mI*3%bC7k>RM9w6h9I%3wMm$Zt|}}GdTX_*W~uH`O)?N8^{6# zM_!VAh2GXn9y^4FpYvU~0Dv+xr8GoDMv`+j6zy&k9^bwedFf;V06X4-01X-@I`db- z0o4cK>B~dv?B2(_vHLtSqAlmAOeOQEz=>E67z;sYqq~t^Xo_&rQbZQ8gZnxfASZpR zP!pXX31~zBp%9@313(Z3^q_!v!bMYSfPofUf*yW+l!^oRDdXS|Ju8QzynfgYJnA}k{ZQctT6%|&fhLRq1VZ}yVsP#JU`cy> zXh?kB@!dKmL%J_o7&((#*Vz4&RhkD!W|T+F>3<|%s9e`T0D%z4!xI+5k7UD35OhRD z2%S09yhQY4bS;1KlUn9yv1p)bcnF99H2aB7lfFK=dy9i!uOXT3_)bp=B#hHr*ZuqM z2GF=>_<2Vll{3mPbq%x20!E_trP_6lFx`#{&J6#z@he4sB6nqb$L!*HW+^9M=$iUn zuG>xBogg4cEnHWxDbONn0Ix`#(9c`l4YuUK!yJ5dU_^KkeCz8m$C}mY93&gMq z*M%c3fq##wOm|JP=+?a{AMGoS0>9Y^5RIX$U5`3fSmZgPvqW;LYrJt8qK)GtXm_LV zX4~1xu(>+rj3SD?K9cbhV*%$oi{^kjdijh@^77;UT~@&&DUBFx%tP@KKV0;7rzeU? z5}E)Ode#&X1u6Xnb@>7!54=YHuS5a>b|ZIj8b${^HLvGmmqJ#n({TJp>&|q2pHtNb zXknQ5K;=S{e@*>)dX5oFsZl|rO6KhT47!GiaHaoY9D5e+0_s&id>u`Xlrv*vb8`ic z(K?qm3Il-<6iCj|jOakkhB1M#d~>Pq!{vd@gCWS#xzMPkJ4FSMGgkfn#E_2zVA=J4 z#{bkCw-K$e?2h6Xv|+`Tw6@l1bCo*|dIlo9lHP<~YTe<^(A? zP6D7*Gu7C0U`(xJ#P{_p-I%PQLu7Sn zMR8da6e58_+4WKvilr#5mWxr-SA2bweuzl1Q7gv)ih?WRbAKw`8t>=fYjoVdq3qX> ze6W$(j*V4*cU(JwoHx;!e)#A1oT=TUq#1^r*qWA3-9oH9tdH%p%cIjwB?)y?ZGFy? zaNz})&7En#PFo4+tl2HnTslsAO*I>w~gPN~*l>no_|x5m5k>>RAN-MJ~np7tz3 z6%i$APezP@0BI=zAO`Ui5C{7u(kWj^g>5HErxp0H)$t0r!+{u$hOPCll z0>Og?1sF19$%90YBLgU+ixg3U0bG&@834#ISh+>U7Ym9;K*&+5flxq{1;&P^?HjMl zw0ilUX4lKS;5&WqnZVk3JMS}$5JXy3&;vFyP24yDaHF{3vld&wG0Ceb#$EC>4>A7M zUCo-7qZclJLg}+)t9jSNoAT?G=wwHn(1?hN^K&+6AP|1i(X>$a-qz=sF1MJi5Im7d z-<|>C5fDwMq*OR#%ZAu&&-SqUV>*M(ggGnP%<1*}S!q+_uimzQ^Pf|&rl!(;&Hqs! zO*aF#7Q(?_DOU9uVt~P$nFI(_>0W-`iTK^DThSXZ4eN{C6xp^n+}yi&UxC<4M?)!PLt7*Mi@XD|4o@Z{c(M z%?=shbQim?U#Qlkh>s4!B#>#~NvQM;Yk_sgSd2l`7@~uzE36cc*r2j26%`f*1tA3< zA_W1V783#igCtCn;RFm70H}(gP*+7nQmht8q^^&C5nQDr(LhnO6^e?KX9|aV-q`L| zk|UeV>p#ba$Y(m_ke!E@!FINxr2jgb8xw0d75=>1ztZNep!|EF}r zm_UGA>myeuYwvnH z&mw#Q=WFn?{d{qb2oNG%G_}tAy-_JOpu(MbZfe&y@Y{CVtp?72ns+z8PHLs_!p^B~ z`|r%)fdVI4v!vaXcP|!XBi_+yIPtN1ZCqT8{72S?K@oG~>N^e4LaK{jnYwuHXZIa_ z`~FrPnJs!GnZ^iCHu;O&w}-*2+U{Gdv1@6w-BZhrthwekYAp$@4Yu9inJ@I1=sD(2 zp8fh;`+lLZFEP*m-?Z)KFWsQ-TcG~M+Pv`6c&G7BB>i3E1>kmDU7wBd5-|5fA83YK z)4c}4B1ikh_gIZ-qtnC4M}h=E^8xFNKWFUXB7EhLoe_xx)@Dejzr@2jeqa&_SjD#f7NDnP5$$kj}r|+Xio#h{ska^+N z%W4w6(F~F7P1%bcBV;Ep|3paWGkpDDf5>!#`0k}rAae2BuP4sA5G{0;N7%RwRIJ$Y zcMR-$CC~H;!DEeBs@EyYMECVqQ`;pjP$I|T>T;uEih?2MANw(s? znC}<3ijib#zXVrN8jQwb!bIcGI%&GH&G%(Jz|!ZXf7(kJSgs^jjMoaS%8@7T zpF_IuxFV=c_8)tyl$zXlSUDMVFYCz*E3oV^#NMwkfe{OUKos8!a2DGP(?{i(g=PJA zvJ~j=n9VWX(mw(9gT1w~v3udVApR`e*(s{wUH{UI@=LhZrKU$5cV|7TX+F?EfB+-y zuYAR2UUi+x5_DuRItp~`QJd*j<*~N?PpYpWpORf1&$+!Mh=2qTos^G^Fv+t*m-fAL z2I!l6vbe95N;;lc)}a4vKG$Zb=zys{7PbuDTzQWua*tC05fK8?Na{UQM!;^&0taaL z_ue|k31k%m#{>m4ni_;t2I$chXsA0x1VdSfCm8_-!G7Dmt!~1mMgph)QBQJ$okRf< z(e*_ULs`yH6+6KIi6R_d4diT0w=>uCKM(WuHq0)-hsmvhBeqnDqPn0Y_~ziEt&LtO zBEYoAvcepEl4$OV)nIBRSf5 znKNJE?z_8BM(;Rk*b8~>d;h~2l1Mz;#7M6cYBC$We*18<(q{d=Qq$B5o)_!?usj}| z7l6=T|Bt!Ra2z-bC}F6KcETWTLQ-zh z4Kcey#OQH7>u#@>%U^!JTcP1ua4@^q2%rMwd~+=gWIC197bjWtbyEeSZHyz(01SW-1M);b0tmRE0t6cI z{?6SC5&#NDh_Y{E%`U~_R~unF>YSL2=|)q|zjaS+U^pMnK%Stu)XcsktB?hGcgOsR z7_P<&Hl5p}W&i^VWekEfo4ZE~Z8F6^(hP#Z3gQLK!x z5dtByVfml3F#RtD5r28~CF3M8+&udlm{&PGpRygg zVvQ5F6vi{jgd3D&Q1ciStse}{wxt=K!ysss&Ouwi;w_zkBg(X^-sC- zRHI)FZ!@@rf_h%R7FQ zWJmoK!_uYvrWzb0cyv|uFF7H$|do-&5w9>;6>9~jDTXYxN6i|R9nrU3Y(=1B zaj&pjr?$U_+*!b8uf94C#GW^`g>{u-Ao2^7n2+Kn^gNW_cLz6>xA%VED}7qhg0`{t zj*+9!`I1+us3ev*2wGnIx0@*|B1-jq_Kucy6i2jFZ)gmB7J-Q=s zhC)H~aSu>YC5<6h%kW|=!P)ZTRX@jg%cp#K-0bZvxyS&BiO)S742|$j`7%cOKW?vQ zhdQc=&_K+YZ%iTuWX{HQm z1PB5Ur!_mny)B^lh$I0hy!$M(nCesqHP4PIE;hIbi0F_rZHV%^NJs8&Gf5Bi=sE7Y zq3ZC8bP!Na%d4s-5krvF_tkPeWgnvo1K7lfcWF{8s^d6#dgWDxKTl+cfe=WaX|Pc9 z0066pPT#0{nhqttZ<+8!L_zW}0Gq>AuEJ!#@p#qHDRqc%n-`N~z6F zm+i~nOjv<6keN_;gZ5&s2K&1njfWcNQ|g9CT0z4J%|dnb0t80DfFMQ2u|jd_Z)le5 zkE)IsshU?f04k@*<$h>g4_T8Ow}@O{6Gdu&CJV(i|8q2vsU0&li_O>jtj%1Yow{(J zOSI_S<+x?1pns+KuADvuOS4SP3H9vswG)E!zm<*|RdPuL>kBFu1InW4jqGq%9_Tbf zbd&xUTWF?lklx2@OL?b5HtS#u+O;Esj}ds>;))=)VcTllu)_B7zBi_0r~t;U3X6?3`J_=szyh zmlua2jj{rx*=jFV3jF{k7Extm|9jscZZDatOStm0Bs{Q71Lgp7D50t~ z4Q2RkF*MT%cwmTu5TxLgs2U&s*t#hqMy>p{Fm=|s?*qlzqqj3#*UcK5Evc}CVg_#g zEN*ZlP4I0qTCPUp@^*c1Bl+%QaPK|!HHHt;W2LM<8QAT-CMly&x+eUew@mt->#nxu z!_Kvo)kreNVL2kdRsjM5t>p#U2ux4l)wu3osTJJxPo9l((VIg|#op?=_PKVU=0aaS#Me(cC*4YDO1rt*YO< zP~_73uDTlUCrtkh>&zr-hE&Z<`)uENf8QE@Xu5vhopO>`1`ptD+=uT<;B>scN)wl`3+cx9CFXVe*01!_&ejl72N}94Fi;oQB zA|_wW5p2O{^&}Jym@5z3cMWT?umFJpAq0Hm*XB{GU*^(;PlX?H-p|OLMpD!+H#ot) zA$=8*?~xdDr*u}+aU8@)mTbMEiWbV)(liIE8x0|ub||XiKY}g^R;oxnDz@T%di=Ia`a}AW?zC{_$dWP?%NZBKmA; z|CQT}>h5*Kah^&!$A9PS!He)(lW)VOd-K$nqw(GC-hC_Jd=NPX>yH~}WLY-ses|`* zO~$Y2o?Esm$mlGF7dpM+;$nAC+_yK1K!_u83_Ok8eZKcC%=(k?rP7fCFB$eHk7A*603lM`ajcu&?45H(AR+`c z%0G-*y9DFt+T~6x#@%9Lfw0gK5bnJ%kmo3$8zL^K1OYCsc{Dc4u`Kqpt%seUL5seI z0RjWPdxilE>?Dk9U7UL30}HQ92AuP=nqGWI8Kv~phZ+U8Q>^3jVF+6j;6S(qZl}Yv zkAIJ-XL85aYc4!7rbfiDFl)2){V6ujq*Y?a7 z1Im@wG$;x~KdFN}i6;tRMlwIgHK(eEUoLIl-L}4&HS64<>uIj*729s|&t`n~03b`t z^*pUYvOntyc}<0WI$4XjqBO6)S_(o1_M?jF=Dm2Q8KN5}H`J}K=ATov*~_Y{?a}h` zeQ^|1vv1Dlmh7eogkAn~%;IF~_uqSbcFN7D%hF`@yRfd#j(!~`C8f9|vjDUqujhVVlnNETH=vQLP`~ zDCSbNf|M`b(7c^d(Kgg*Q5@(_A77(*F%J~rTvEL9-Dub9YNaeShEHXpQ~V0R`yFl^ zF()1R4SOqO@aAfKP_2$si~E-+Wcx0zXDhT;d#3G@8o{5v@FF9!I=V2hT{k_y7VTy;8+vR+phu9Pou) zn9WVkvJd#Bm9eIM)h?o5TWio5Z4rTQ8m~03vfqxz*?t;;$s8SB|?#{634fl4C%g`@d>u zuI?MbWjDc+x3zcCoTGmFB0OpM-i-67r>DQ@Xyi3b3a!k`lC-mCYP}|Awu^U2FN!iP zZ=Q^=e`Z~K0EIA)mq<~;&)qO;0S^u~CT5WB0XyoR;Kk!<&Q|JnINIt)^2 zZo1jL(x=xiY4+A7dWzitC6%(n9l)^r{cq7dd#iV~G0_z9yC^IIKTv19{RSw30k!sX z7bvFtJx$b~)~L(3SzPnGq(lzacBe;@*C)tuj@!(i{}@svd(QV4N7KiDIqEOVm(w=N zp~*ez$J1K?_oPe?2x5qYn?YYwVUNzrC!Zup z`|PnjFzut<*Vjt_za7ZxTor2xlJTg4st8o)yME)EDR{R7%Ls z!gBS0;6GNzX=IU-ai=rbUT+>x-@|S?J%0_U@(sL%Ap#JDA->=|4}jnZLJ%PcLKtHq z{OxU|kdjH~VUFiw?Wf-^t@du0i}E_XOpBeny~!aYlk#Ui^;O9sB$JQgI=Pp0-)0Qk zA~F7fC^GNeIcL`i3d>{5S-Nk*u@3LBga`xt27uBG0|3A<5QHHOXX(OpTfV7;*Rt|R zNhH*c&!yr+puYUOE_dzoe;SR|N%`&wWVW=)CcZ=Nk%vr4N2GuNj0OTALa`qz`Fpgp zzwIO%adU9jwpvd)KyBo&y6#KdKWyOw1VkMxoF*GpL{@Y$(@U=~SOtiCOnHT}+GJ^ulL* zevya1&Zqp)(lm(ZX3||qn2-PiLCKLvX9CyX)7=kW^G|%Go`OmK?XfuvZkp5hjhlxV zkk%DGZ2HL3j~wP6qC&$WKma~`EXHUM1PBc!6GCSnod(!)GBA=6^LZcw7#95+S*H#3 z17FeAf=1O1Ui+)SMxkzWs{9QRMQYO#ar)_RS1hh5-fabjG_H#@=K%#xsIqp#@T-uo)E_E<0J6^~ z8djF(On7H2p!7OjWL!EcI_q^@zX0{hw&Y6p>X0@b!ttCWSde0-O_tMJ!>SfFGpJo% zq7m<+7O-qb-$4){JAZ8}Hub3mUWL656V({)K1?(VI={@X8Zq}J5ar+T_S7O9BpK}R z-lA979C&#v*T~vSg=r0!R2NLoLy{{pHgN6vnRO83_DV;}AhiuZxwz78BUcMj7cI0z-dhC;57k6_TG6)cEah2d$0sP03+m2jSXCu{+ z<&pXPsC0B!&*gs`k81_p(YmJaN3ELwTa|dWz&U>F@Y{d(sU>(Ee_={SZ!?hauvsr3 zmPb>2s72irAKf@hZkwa>4&RN?alKq)Jxd;zN)(Em_y7!U71qW*g&+|EF@*r=-NyLM z=c4$S{QkRHya+^+ci2d`bi8Y%LbQx}lTe5(+2e%T7j|B(=Fsqaqid*-=P7tTWFcgz&iY}+HG7ZTmJf9CBe8@(%L!-6`=#2&zHQ5b3 z`ilfWQF+>)o-LdVG0Fv`+;f@B4|>p)dTYz#FaY^2gBn~~OBWa*A&To=%Xi4yCt5i16+p}|CA@zoxL(^NK55YP;9;7$ z@-cW#9CHU$a}JCBUL3Z@S^;Gp$}02uQhC$4EFpx_C$h)a>-4TM$VAc(4>g{}R7y>% zA<{B%896j?=rTt^%E^f~^ausb ztz=@cv|Vdq;u zLB9uTw&iXAZ~xr66-}(a(f=>%q(HYRi?>wC?h5tEKPd;b1GFa&e!+n`2p}lL06kP` zIynyFO@1kxuot?DKh+D|h!T#fNL+7e|1l%j&C~T^ z#)h0~m1#qcC2!VxX7UZUaI?qWf)R*-k@ZNAn+wy~={8Q=E3)DAJdK5ax|}|D;Nbl1 z_pTyp|2M$QoC)ucWCRBT&_i>%g+vIcF)n^PG)Ui##^%K0()+<7{LIPXaZ&TyQUn0u z)phtLz@YlpPSD_i3qSxBvGT+EL_crF;MztII4~ zZ8}eh$r6Z@nJ_6Ya68AW4b4it6#FMHzFb=TkxDWNJN^}ODXT=qw9~;g8KQF+LB1{`Q8Jh z$%8-3dRxAgb~|K5L|Z+k%M&xOht7jml2l#m!TxGJH_5G#4(^QuwpY&32rN8$ns1X4nseZT-TBTmJ71*ff>JJ0^ zjyy7$rbB;V z?BeF~(*C;n->LFY0Dwbo%06m3L1;Os%aR{Hvuzdu=CWYSq2*=1hT=>BPBKmZ^d<u3L;_=t+r*WcO6D;!Q-b0Uj_=Tdr|B5%vN$8BA^PuEMw6tOMtmx=?~Nqh>$s%r8((X&nq^Dl^T&qn(ley@e{nPGkA zes0zYcpM&O;&^KgP8T#J@SF z;%+03pr|=;aC0p@&wNvPUKPi^EEKRWJY198pIG6|$`Ekp^!cY(Je2JKh_hl|hUmc+9ouPMhe(2y63vH#94h> zdW=Ws$MAdU%*@5;#?Ib!B^HisuAI^a)2jlQwJxNhsK%GVN!26qez+Iv29%tmShYjx zRnO%e>mUvrhdzBZ1NGLbD($HV{taT}?F`2(*67}1f72Ui;+spvKH2AUu*cgNFWn*A zH1e6-sh*2!vpK21_lt8LZC!(3t9AWn*cWQ)Y61;!roZZ@kEH?`)7uBgfafzoZ@Hy5 z0f3x_&`&8hO3{_N;Sk8adoNGgT`~5BAF`dT`#Siwx;rbPAPAu`tc;IK*X|#ZEayhk zn&a-%%zhkwzRx8jQg`$iOyxlDMmtPyt!7^!3^Lr|0)H?NA_3XyhHbIk-3-ufBmNFA zKM5zdG%y6}5%eF<7lXZqK;QW&(U*Jo^-lU^#ToaLv<9sr-9NT_d3e8{OWR$@!LHC} z?KB)zgNw(mGOAPmIP*0AVp>=*voK+?9-;Po|Gm0(+dhk!$!Piex^|X2o`6(Qe~H1M zy_c1{eLNqnBi?6dT?{=+;SeIFjY9JuUZ>Ehk#qT*lKE8nxCqRV&P*)FPq(AJo~Hb? zlJ1jEvG(zN{GWTq(}-K?Dc+)?M+UgxiNpp#nG}(h*`StSdhf%wAn>@=U=RTb(<8ok zK#bOEP<@;;|F9`DndeGl(Sv}70%}78l~&akJ05Di`qW7GyeajZ4^rK8L#Pa z`qj^0jL>JYHF{$LSmtf}p2vvt@^9{es>uc$rNnD-B!CT96Y(HtW%{6FA%H-M^y29e z5flNxYn*)d@gr1y+)Br?;$ATRVQITx1}`+)#NUSDBWVEh*BUhS_h zlr*%F&s8x>EpOY!J)JUZE7s zFdi17&HE2#_3ZPX_n+))@@xMXXdN)tkGrLKB6pt_0YAjO;fPOg1PYQ>iK%Fe;{RjW z00G2^y#J2HQ6HAih_H`~SLgEeV39DIUSDR2IggV+P=vooga(Wl0T-^ts{bOhd0Oh8 zYsi9(>cV@srlzU(K6$awkJa5>O)I<)yMC*Ub6{{{Qi%l-1v1nme^7vJw0~9>2d!!|BA&4tj4^L;S$svpTA_UA(9fJ4at9}yt z8oBR*T7tF$5FiW)1Smp*sTNq=JcJQx5qB@L;U|`2QmTJlqqWvB0=nYwBl`u+5+y7Y zqFb53s!3oH|3LtvU4|6CjjjYpRrSR|2H}AQ3}-M%Lq#EJBpNhu5GWF28UDwRB8UjU z5Cljg`ar`0hIqF`j0?8C%tqJA_-fS_qvS0k@#`WvexTBZyBMAV7;5wk+XYKl}7KHbmmWIaNkw z8NKKAeQ1rR1mG?UvlY$>-c0Z=JFEGOPvy#K!LNhyn%m-S)#DH2-|M6;(}C0AjMb)k-pSa{A`6 zg|rE{#B$ZJ*7r!lVKg^XmT5NyiFFN}Fmg#Zp=r9B6L2-T5y*3Ucl7IaJf~UJVLpx| z)O0^mB=eY^cN%0o`&E=%@A8-Wh5%16-=i}BapyYu=lUoOg4*=+1^^bJjPC2GhXWt+ z!Ii7e>iHb%`5O#ZHc3~C51-L}!xfYGqu~GD{Z4V0;gf0N(Ul8YH&cA~)|gGp|0Gx+ z>p9)a>Bi>*5xOEoWVu9->Omv5*DuK;PA*xK_%CY?={&fh-^Q2Pb8mmiNcdV-eN`p& zypW&G@BfsX@N@H_30@V`&WY*iwCZz+$Ol*H2$cr3 z*C)s;j_7D0=<0DfD1PY;K0GHT$0XYR^eE!wdOy+cfDj-k%<=#t7JKbSt)TlmYcTL1 zpCrITyte-Qrvsz*`_8OH?u|599WazrT`Jv5LOqDU*!CTFcIfZgOH<5=jIpxedq8bS z%+}g^RFz?4X}4G;xrno{MtoicWS5EsSs4h6$Y-Q&g~v&P3W8$M>1^tb8Yvs%m;k+i zY(NoOoX<^VtgK<={@)txUI?Htp52i`Zo%E^EST(R3Hy<>Nov^#pHE|C4%p^qwolL8 za?i9-^>?6wNuN!;LizGrxV*QueXr?v($^;l`n0C?<>KvH?jR%Sf6v|6?N^hswEvX0 z+1mDfhW!9(IPQ)70k|4ZQSr?K060lhM%o%gq!mt zyXT%c@Jn`$2YhXvBoePJ4upAp z?s|Kp#t-ysb8U_?$WoC#Q#)MpUckpb^l) zi9Hoq4XT3pC`APVf>52_@vmw>8(rI65!f!7k0WpUa=ey;LQM_Wkr-ijj$0~fy2UIu z>ioG>CX7T=NSMK3obVAiO|?2amz8rptTsbinLGB)Raa$ZS0!Rz@?0q;wF6vVi^6mT z>B%cug21jtnp@2%H>=IH&IvFWg&N(dWJOA_NSyoa&tJIWURL#K;A(o5 zB1^5geK-%Z-tDXi;C}93n78q|F1Kid#5z~YZ#LoN^j&1ot?S%6$RLP_hEpmy1VoGu zjMC=ZMRuAi+$Qa+ecU>!PimLavwyrxJWG1+XltRmH{8FWquHy8IPE394nYwLPd^I3 z9hnB%KfhL*ruQ+wW-;p>tVwZKfQ|PZ=GWw|KI<$mCI5GAw>5X%y`KI)7)2v6mw+lB z1yN2g5)9uI!)u)$>DXJB0|FkGQM+Oi_C=|07eYD|oI)x=B2aOcOyi0DG}!Z%`163FaU~iy*{cx`4=JP%IEA zBq0yk+3mg8q26bCi%zbrt?>cAi*f}5_N7W6qqr~k`?~N18?ii;+N3DTJh=Jeb8sQ56!Xq0wTl$#m8KUUK$D z((@61FGbX(q@;I?m>2}X1SRueAsZ&CSe8mah9$ueh6FC#{syGICD2r^xQS||!Z-v4 zET|OMJ-$V;S0?LJ9x^1RK%FE#HtJIv>i2)@CdpLghDxYeH>m z%y)vdpjtD_kmb+F)pgrG-mgEEm=FXA4&GW02Iv%eWx~5US9-07!ly@4Sp zIF-G=EU^(00uNbGQ}vMB4ZPJ~koA-)>AOIiyO#Db!xGY?X2QVt_HDq#_xsNOoz$%-uomNi!_iFCO+qb{FN?$#t z;b_PLA}s#w%##WhVw0BQ8G!&bXPd{*Pxa}dJGTIR9-U9~v^VgJBHrAzyrBMJU^*F6 zWn0Aa4?#AA(!kovkJa10^I8NRLfU}laVeVP&N4{EWV0xh6@Q;W`SWW%@`*Hk1-XY#`ZD5^wollV=BoSXszH z6(VN*?xi=<;J^ru_ka(yLhWTNW*lgy6>OfOXhpi zh0&v7U53IUESuUVA57v_tvmHjT-znnc^P5~f8G=1?z&~Nr=~OUlRO^6&_;-|IwB%2 zzq8Z&VXULtb~8JtoOO-D+q>t9phfdywn{fX#aI)eUH~9LKmOx@2R?nKMy77y^@PvF zVV^he^{tC0@wp18>OiKwjrF_*7h(lxhn-KWee|LwwGjYB4(XSB+Fl30ozIQ=Sna&a z;(}nv1PZ|{J93qgpM*1)LVN%`~PodorfL&G_7Xe)wOmIv7cd438iq|J|F=X zg8$Xd8ros=2>#gn`}{jfG(M9HzF-K^nu#H)EIwyObMA8whr2pQm7h_VG#xbMvRkvI zO`8;!*F7zE&4trK!0?j;3+Kt-_S$T5>h&jm=)#1W_}ip-T`qP>7eL5!0$6x=n0RyW z@bK{P@aOXe4k?_28cY>RrBJF>Duq(1_hGUBU1R6KUwz&e;LiA38=dRu&0YP@67%z0 z+CO11fQpbWb{o3;kUC zG8>fY8@Elg+NuI(h8ckF`vn?<9wJka_0;cfGZ+oJ#83neLp$X-2)z8}lw*XOKa5JC z9E4HgA|s6xjTG%UB@}Z4Y5DFV)4MdKj{!+d3|h@Pwtk2?CqbSrF*h2=tfmkeZGi%* zFm#Mkz?Smzkvnjzv5eacSgFY0y|1+%INOHqV502SM0fPkE0r$lYchQV-Lr>}GYPYt zO`HE)Our)k{)ojau`ptLFDWF3qQhPA1HIgT3%`?-&i#2g2w%1xmZ2nJ{XRp8Rky-% zBU$7yS2$Pog=*gHglA_nrAEv4 zX~UDIhIoE3m}&h;QlV6@Sr(j6pP5AX?`h!J>(-u5J(mB<%9_m!Z)={0cd|=xN>Avy zD?%z{`9FO~)+5?^wbJRJwE5yUZkXFbAVS0uF#-k=4qsT12>y|)67*Q% zEad`NEtDF}CIVOiD2YKRkvYU)rsq7p(;LHbnoD~i(fc^-_Rn`O3|jp4{&>DZE$<-$ zgvBp9hyxhp_qp3$jpD9zcXicukYQ6mA_4*``C8D#FUJbmNB60)*{DVAI}*t5H$ywZ zT<>A|EV^-7Q7dMN;g}-*J9__6<)ukheHbd|v&t4sOYaEx$asyjHwq=InUI9L>D0j+ z{qSn?`J?8=SnpiibBej&T~*Ul-*N8FDVpiM)Xb(md9mTc9Fh7+$dKu!o%iTcUy#zf zos%R15f`tc6L9uria$fW!Kl$rnCCTj$cUhW=pXdzo>_I}#rc@~v@Q!Z zo-aB1J#NV_*XZFRp}(pxcXhYFs!YbgNe1NCR%02O4}JQtbwuOG;ogsZum0y+=i(}$ z{IDtejZD4;(@NP81OP|jzyD6}Tx(wGRFD7(H@Cl5hoZ!Q5el^J{xxexiTb{Mk0)g@ zn%x|s5>(6xl_Kk=oRK6In~s{k@0YhTJQaFo6#e|%#WCOgd7V>9_~(-OV2WW?-FL4> zsc!AuV$YzqR*Z%2B38=Br0=lUeDy2HXZL7;LAZUpv%4i>38(+Q?*~0%wLt*m?)WQr za_6vaubaRkP9i112TV4VzuwDEu-^LX#NFd)8`)7vfyiWVL<;ob0vpV6kL~BgDkaSK zgHF2}y|8?^qj`AjzGZU}HTm0!&iD1rcVoJ7L*Q|oq)u1XV8<~fxK_BqcAuF03!jar zNdxMk^||~T+v@mTp={ezC}D)*m3G2>L1@=&6*$BQg8Nf@&tB;z!xQGZ&R51n@js2o ziq`=a=NNjNPiP8$KKG*Dw7{!|)=9Z(kva~S;yHZ~gy|8HT>0Lqf5B}vt9A$QdL&#qlZ+RRXHd64@(Orb6sU>!ax(J?-S z%{zSZc*EZeP4FKGV%+qQ@Qh7io;A5~j*iT5W3P{6CMSI`BnVlZsoa?3*^r+*<6Zb+12ZRld)N-KA*+Ztn4z;0&#@RL~==b zZ|WQ_DkH?&E;pxT@N{{!33Y-*q9l^wKR__o0wZ)TrP;ghcb-6X7R@OqQc-rwn&u{`Ih>l{}- zHbrWd0TBR0A8=%dfe-{h5J~kyOU6%rboy{aDXb0@x-YbzsEPAl znu+XOoi`;}r*-Z<00IRl#`9E#FIN+wJ|Yu<4W{oCJIT%e4?rrj01=gp%}zEQT%G3c ztmHThJ{Ry6diTfMz+X99%qj)CD*9xEgh)t5lq5U|z>a1SwX}&s8R1G$OCQex%p;j7 zg-}pMG?P3((4{goPgb{bB#}_Z>M8}O614;bwx|~Gf8#$+<7kPs-Xc*~&b0#s3FNB( zt-9lbF)#q-AD zbSE!2^^xa}?LSwhDZXq&TaQXf`1zkqYq0@Ri__}MCo~=UW>;~U?l`=)=Kw4-_nl^U zLud2JduRB^+B&;Khl5U;bQGGJPqi+YlXedf`#PpEnswzqrjX1_*@6HO0(Cu7*8(C0 zL(^oo6&OXF^fK>XT9crJQGti=F;v${SZ@A&;!EQ}a^s{F!MTMp01*Ndqoz@?f%W}3iY{qY%?QtFlvwLoo=kQ_aMOQv1 z%oYXVFTo~6l?!WI1o$;iwtbvPn$j7*M5hNu8yhhuY!)|!+mBH`;Rq)IRfN1usVKw!c(4;tn_H+aard1w9;28@usw=Jem?s%{HaR zv4B*Qz?oZoZ*xmxUPp{#Q}EH6^H`1fJcDD8M|rkaxe!SrLZ!+~*b-7x+r^(2s@^CN zdR*{CebHcw>85w4nJ+-F&|fvx$83Q6IeM( z+Ej``6>FSdv_J6O7%;0qOzTelJ*96GYQ9SmIOZ978fhE~ewL9078~l(KUCWz7^_+7 z+WGblSvktBHXJ48GBh(o-Aur9u5qmNxvdLh@a<~QAVuZg=HCDT^Z*#R z{0rqeR4x-jXRU!EaQNQS3Z8bKM^*japk2NYeC7}$6utmN2&61_^-2}+x*V7|Em#3I zy_SzhDvrH&_Y%|qA|eI4_am!9J@6*zk`qAFmKY;_ z>W^Ka8*YbU2!YYvVs)BMB)22s~TK?91OO zX@wXMtjQj}lCkf;?qxrG5e-h=7-yLQ`){Fj59cB%3P#n+^?F+^kB|~Y$LyS6U-Dx6 zUl|aNpD~{8^->d1L_M5FU#ak|e9ku|kJfa)wdqprB$(SS*G;@g&qLc1*Tx|yv#cdR z)pesYBP}Auz4t;Q%&rvq2`09irU`K(Dj{hveBKC=ZxVcT5p@;`K{!n1vKEAwRHopz zISY?#2~DCSZqnuNBwZmutwA5lrsQf?OOhxrJ8n-?$mwr7uLX?kth_(~k8-+_01*%c zMuRQ8Qk$KxK-QW24XmTL(eHkZA}(V@`^muIIv-ZHz0J)=$#nhY(<9 zx+X9HIFB~>(_XdR9nZftNMu{g*wc)0k;@#b-T!Hx&EB>Uw zJ(NOjJ%uSD%rYD>hYTUZ8XJflLt|~W;vuHS#@lUx*xPNk+(Zb5A;d@o$pREaxdLKb ziNlq2xyfrLJKU~1B7)5+DJdke6h5D4PidIIetzDY>f17+n+|lC-NJ;HF{?{LvomOG z7QJYWaBz{>!M5DTEX4!a9#H@55=3OIe}R9p#w zf#nKZgkb=0LSQz_DZeC4Rt}06C~&w!q)ix+ObA>cDBwiEi2?>^yVz?Iu^>RVB+UOV z3uW$aAh9e=Ohx%B5~GIIH7`p&wl{x!_2qEAkAoFnqPU}FZl=1VG6uzc&-hO|B0C1mC>MTYX%e$GhwK8t>p-d!d7Q+9>cC>L124i1U<0}2(nM6F%bwZq=! z07Qs@3y0 zQ?!Br058zJP4h^AB7Jy?0-;@bh}!Okj56aOaOf{^XPxDub}P?1?O*Gw~d-#_0FX zPi)Vd;bfRS42`FiNad2j76`nel%+1kk~;__P9)gMqAW|*Ghf%SR{#Js5J+l!r(Yzk z_5FM$%E=KBB5>nY|8e^76%g~gpJJ*ik{!RjDC75{L1muDE0Hq$RnmW&meRGVDM&uY z=K4x9>6&Yc|G~bE_53E99;W|Xez*7OqrJ zs*`oErbv5@nw7}l!G1Dv7ui2Ih)1UXTEZZOZdVx!a!--LnTu(w~GH_e4u47}P+i824(Gy~fHq z{#MB^L0I054*V;-a{ni0HEzs$wLo9-fS_`jw{0hN;{G{XT?u>Mtn^4o(Inkr8$?(R zf4$)}e_!AYNv|~tVnF|cK)3zI`t09KI$kg5y({aVPA|4m^`DC~GsGasSXfC-it|O_LaPO_<=_0-Pct0%zCj!CMmM@}% zhMI9(!tYmIAC0P*A`|2YfB;aJqOuDg8aoM2Yom)HpL(nyN+3*P#CLjputXedLNGvx z5Jn2%z*op0O|HiCxQt|{eR|~pKFYGDck=!X#A8m;nZ+L&inZDLN1wZU-fT%A+DO#7 z?4Gf=?BXM{ti&SQkK=g*L^%xZ!IiVKhL5u5>f$pe=n(I0VnkMsj2r*|eBB47^4mwj zXoiZRmv4pmNl!3-B8}0b+AhBy{EoYRi7mFch=CCNan(IvN5|fd%5kx8!KJX#v#sk> zt%Xfk%{Kc<&V@@00~%hKtKUVqfl#vLK{FdJ}u0{3_93Tj(s=vVG5;)-M!QLr}W+%BEm_+<57?z73@6(zN*}z5F+BYZ?qeTRuEmd zkOx^;FLRRi_uE{fNB6t&IV zyx<$+I#!~mLINcsBqRVpfr11M2oREotc2#hbffOrf2CWMpO411rRX6tYM6OnU$oBJ zArs51ZB>z$>SVaEJMmBw8|3lf0WMmd{Y#v&6A%lsvd(XwqpA42CRk}=mK(E8t_qya zo5obdC3P!yo2Ad3$242a;HAC3akz*N)mu7NPQa~gpoEV7C3^CEub#|2re?TNjDI^I$uOt$B> zU3kB*-O|`-n;)w3A`MH~@%?8(s>nh~B!(D7 zK!7VQwk&vV$bks*1PH0)nwR}_H~)s8{@q*m|I#TEPw$qp=^9w5?|)mi|5}$c%VE^c z3Tb})nbT&Fnp1H6rzBxPPtOU7BTD}#oSMqtUzYQG;e+e4>y1lNU$)VJ`>wBp6 z?mPa6ZjP7zh;a0-;ynF>>wN2lq*EbdjDEeF&j`PT_N{qqcQgK^>#}olT3A1it1agX znyxR`8<{oLh6n4qDuH|%zG>4=38FNIedU+{*E8yYo$z^{t+(wyTrTsZI57o$We?RN zLNJgdkp@Iz0zi>gMFK?_NFr}*;n!bqCe?^ffa`Im=IPGB?_*jZ~i zAbQ+vZzBWGvgegXv`b*Yv1b4(u^!*w^w9g^XS=pL1PBo%q(w9%^ssI3^Q$t@l=_X_ zGLa=N7!TK1%duh`2{=SyF$P>6+uvgZ_u{eSIk$5Uf6tNDT@qd5eR|qR2z%4o$~8&wvOJic$`y1ai(>-Df$>UIs5nLNDvmW z4}Ehj7_2=zr`^fg9ZHr*2cbzjSa7{o&Z|n13WPW!TtX3AC%t7BUD6!y3do_bRIUN1&Ga9 z_t$I61Q7Xhrdj)HVoef4OPz0`>Hx+-Ilp+mD9HELR(2IyluNwigx@ZpP>;c`_ zWrw448<~aRNg0|ZC*{9yve7z) z<(=sx{?XuJ*QJ+$G|@vbaO zmgm}cE|C<>_9)Y;W28Mh8&*i2@$=;+nY18Epd^NR_}}dTwuOWIS--5d-}L#HHDOkP zxOCllVP2Du8lSGP+Ysuf<~a{_*V=5%e&-G?M~iah01P53z1-L_rKoiOEYe;rcAkq& znd4bdkbt&PgE&&Pnpb9E%8F*G01**sur}++*jhTx_1aEbHNLU;eAhSNO4wfMYL#r` zc`ExQzx&PAWjdDFT~C-@dw!>Pj~0?_Gw=VeNNU{`Z!*X6__cOo&O0qJiWBcJeO#)9lmK*60G0sn-au`hYRJahz70C+QUb)W3@6t9*u zlIPoiV<1FC0FG^Z+P`V@noWBYFKI?U^wA!Z`PmRE=a>3EFS+`k&4*d?dwZ9!Q?O%r z*gvzj@vL{|-<+bCSGAxTI7^QYS`GVom+^8wr|ZG& z#3k=F#$Sk&EBH~lS=EqEK7~loUnuyoJzZfBezmnh3Mn7EIkxi^;T%PuE2hF^tbz`i6dngc=xw!5o?vzEFgl(& zQEe=kFjOv)7b)TjifJ`VNwe7$wA(}<2S@7TEQq3PJDtb)Er3+Z**|*~L%;PieL#XO zVg}@>kq{5Z*5r4A0OdXa1X9YVzYnX9sa3>Z)VwuE12?DjozfZ~L5#oY>*WVD++(9= zP)l_ffyAc^A_x#7B-fM;lAcvFkVHg7SBzx!LMY46I>Kz=h!6xT=~!0kkoJ-WX%3Il zF9iisVw{1+VkhNl--wB8eo3yePu4?a}SF zH`1)EtgNi8sHIU+RaxTL5jub6f4o&RQi%|ed(BfXz2OI$w*h#P4fm*$Op+wvKIN*> zy+2FV&*v*O-k%YuzIA*q4#pJ0kqUoBNB~iA7L9)3wN$DB6zU$6h-eW2Lo>vn?WxUo zfFJ_r*6HzBdk>-X9t%Nj(O%1=Pq9BLy~k+Ozo|QXV}Zf@RzLs|FDp4!-iRCmsFB zbSG3-cmXE@UI6eTol8_4Xhk%02-h2^IEK#C&Sky4e3zir5P8cF*A5CSXH@-9v zf*9*_?3x&5woTiRIdoth?9KC7S2iaTryWed)18|#pRt*YLh`q$@p*tCL~E{-WxtQJ zv+DWt4a=|E1DFb`Pbc>x!BLK;ls($ka~yU%Q{sgC^q;KW$NZ`f&_IZY0S}^ge^Y|e z*MimmcME^Wa2-l2fvG`4Ps=U=2MevJ4{g+D#B6I=yk0T$cQmRvpaHascpz3~LdZal zTCxjTFhH?RI5;G=4M$8-?`f;Wc$f5DUhnbmQ6XoPq253K?g4frD;Q$mi~tw#>u3Z| zFE1+7t27!72Z5l`gIa5mu+6_E3%=c+??lr{ue^Jukl_1&9whcn%t@$592iLxLsL^@ zVzsaUX60?8hWa@fKzMA4d=An`0Mm}X#W&%V9Q8hR#JM6OAR}SVcMD(n2!9275<7<* zas77GP}!C>z2B0hH~d7<4k2&xA?g*L{P zi`uA4I?PBn180jL+H_{vUw_|;Je6xLd$mJ4nZ0rQV)=;PNdhu~mF8~UFlXxXyc6Mh1+TnfH<4o`@z;=_PMvZA{XwYah z8Vv@9-LyU7Jq=26*#ZCoY5!)}#=wCowB&WXy*@ELKEfLZH+!BN8zn(@iwfT;F+dS6pT=2oUAraOh^9mMi*+oVo7OHH7Ps zH_$Ot{$ZK5IY1C1FT~kwd8!{K<|wq}EX^V(<_9Kb-627T(arZ8B8&)#5&(h`$;@m# zJvKLyaeinZ+oCZVGyKSaBG7xz^pmw;&5AJaD2#fx=4R@c`wH^ffbM$&uzHWxalF z!0vD=W0Y})Nz7!L8`m@lH2B9wgF#z^4P$`mWPAVvmYAH(3`Q8Keuw}XnA-4dv?5R* zSZm!{&pnCs8G9+F7^#W41?~f4#`V1#V-Xx4KjaW81r37!=0eYeVj<+!56TMSDe4+C z7emhL-u?j*5dbJ^$N(8S+G*4bC|(K3Yt~K7d^zd;@t~+8g7}IE0j8R1rs*R{fd<9C z8PWg*Pn1}VLlqfhF0q(AtLfy;I;I7|77PSJsR{B6cYhk+fjmGF5Ck|*Se1N-R66r3 z6VKZ(lp}t`-9LMLnE*w?*Ye@G>Y=C@2!eP32_=aO+^XzXoWFpbpJ`dpV!vRX+gI9T=5lDA zO_!UZ(_e!AyyA8nEm!<4uwS@|Pn5Jz6QX_k>_e}|BV;jaD!~Gxm)3PtGM->KpTl>z zdN?Y5)S{gCiYKq9Mu|k#Eqne%L;xV`LB#|>5c6$ovy$3*To?^y)W3)VAzYWATx6me zHFu~Ch_T>wSj|kj=j9&(0t8l#a()(veMi5;H(hxQu?c)0{cN@~WUZJjiwZC7-Lg1W50^LNK8YX|yq;K398vCeIQAv26 zSK6Md8){Jk1Vl%!hm?9-rhvz3(e!@UzICfzG(Vfo&{?6?%j<77FLNKrA|fFXkA)Th z06>+QQLRS=&D@Bk_Ul}j^|+23v`O~|XZg1~>RA3_C>D#NYq`Ga|)uJ!1zqPH6P@uQxv}Sl+wz-&Vn@ z`{pv1LhbYL00b^4V>eY=eCvCqqQTO@O*P0EK*!giX_gzM-1EvoU=Sgra0CH5L+0k% zvk1$&OrK(u>}?LBPEs-kH^_;~VJgi6bCLNFh)9Sq!weu{7^O(pEwo8x zlGUVgp_&0mN$4;jJYF8h+WV=>X_WuJ(_Yvhn6ws%!XHyXhRgs1j*90Lf(JFFyJPoA ze(l-A@O`V7mQ?bv2~B5$m+2K`cb-M7KJO49F66wb2**nL-rk3nT8}aQy~a!Cn7fS`xV!PEC`z7EXgbALXb zy`QVX_I{QRRoX3ht%Kmzdj6BJ-oNtfnNCxK!=R`<+z+!7il$?SXnmDfaxi&!cCAJ} z>{d1>HvQk$JEuipfe{S``e6qbrmaN15CGnpXpvduQGIlQ?jr1)PL5ju7qNR)9t5?| zpX1Htd8wAPH_gBA6pUH8B4h?UjXUU9RfNO{3J==)A9Xgb@9B2DP;+|=J3P13r(Hg; z&tLjeU3rJ(XPtVI_-;An<*KIyt(w_hkvr^lBth3u%T72t!H(OYDZmj zTkcOx#XO=iZ|$r4fB?-#Io!4K~5j2b3<4NznArK-QUW&35}{JcMr6>tdUQtMt$} z?G`d@KaZ8J5;x8Ryp65n&!6A-vAN0o8idUa1cL`^K81R zqK&Td<2RT^L{Y-kRIWD8dHo3RN>C%%_{+QMPF`ttMv)VrKm-Kj9wzTh%vuU}Ap5-h z0wMstFy~G*@`sCUt{sj1x9cyfu&9i`aKAla-GTrKkO+f{gH(PI%S)Y__Hq&atrXp+ zmGlDTwzKiDnSd&aPzVD|)wBy!_xp<|UT^>c9mqb;*8!FtGY|?}NqpIr_zXw)-znee z&S`I_%dTJ`2sFG^v0@@>%s;neTl&{(-#>1WZPKs1ozCOqu|o1{iN5WMFaSYUee?M? zS3#diuzvH(c!jGriBOMO$$zxpSxEUF{Dw@S7&J32Ef{RXV`^znjct$4{I+LwU37Og zZOU8Czy!-uu<`ibv2oXPph9SWdg!U>)d@(?X76kc$0P8VZ`njd+n`EU!uP9Wy{Am6lKHZ>U)&}m z-9`wz)0BKl)jyw7gX=8qXuLNoyB1J_xk7uoPV8%tA^?XiXBA!LtF)6#N3ra6TaIV< zYd1e84+F|vntn3_jN!d{TJ(D2DuQ=t04hEiB$7!aP*4azn@fx9EqIF>?#qDun?@TK zW8UL1Uk{tQ?p&>1FI9Q9eT6&Bfmt^@phJS(|GmQvW({9aR`@z8|LZm22mnMqIKToT zB7F1mLK9o5k3^FJL`0j8XFcTO97hx7{`vZ?n2!f})%#c*jei6P5DH4b5e>iJqv}_8 zs(s+!&gClZsIrqayBR8~Z9U2jw4R&aZ7{>?*fq^(C9J0wbQ`UC;mfZHe;Mu0L2Y^! z6}u1!}hv^+xIbK-G26xixk`>KU5o$@Z5wWj)Wx>Sz9p`OVQhw727EVyW=U`0zVOF?t^ZR#YR4+Zz3RG;;cU z_x)4ZxM`JQ_IH$KJ0DIhcZ(IWkbw}PzUw;QX!?>9P2ozb9nKT~pN?+HCn5{2S0Hj z{%==vB9?eRWx^r{&kXJ^H)wlko3XE-nRTp2|356Q29ln0)$Z{i!}WRf6!G&&-?URE ztCP@R)vYrCL_>w_Sb3t%HfgCxT1xwTk&SajPaYx??_Chd>2lg75dt9DpZZCj9lG_7 zF(H3$2AQFXDCNxelh0JnFbZnIPD7(^qQ53`qg__msE zSJ~IUU$t;1E)TT>?2D^L#zKys8PX7h2tp8uLJ))qLJ)xnL4+Vf-{3fl&12Egf5)o! z_44=G_PI`TuDx~JP6+x4LJ%PcLL3fXmG*i*pBtV&`3OP;AqYcX!SQu~cYp8$y0M_^ zFPGUQB$9g^jt@`r^4*8@)B)pkdyn?T)qgv1gdqq*5QHHJLIwi?fWTld7z`Dh4C^+@ z%_VXn9uYc^`LmYB9Bci|!>TMd5XN?n178EmVtNatW-eYW0mr0&q9itxwsY}4q`&}( zO1n9I4cBuMH`kx_zel}Bf)MpCs-=lOCH| z4wAHcJWjwb{hR2_ZA$jVk59vocx6zHjreEX^FIk%rbzRtBmMs0n-KqC|EY?LM6%_# zvY%z;#V$)`+F&u{``unn)p~jmL_|XM(A8F_gaHBp@=Cnv5yoAHshIpvkpM+4i%Y|M zcN`s4fu;$xK;A>xw~h^KA)b*D6wm^ygzUs`K5ss6C8GcU?NZbv?R;~Rrxtim;7 z`QxL#kpM*{#+lwAl*AS>@yG-Vdev92&?V#lyI)J6o$p1#H?_8fzgpw}ZJ*uP_n}>H z|3U*@Q8s44BRZh0F;gHmsK6MkAH}c#*X~sH0%B0O1)p} zbCFZM_g-5YZ--jD5vSH-ROUSVi{Osc)_A+;tN66nY3J0K*wsAHke=H>k)G1`h?nU%e#ELq zWLf-qw@?5bOszsnib{+-M6DNlq2jNOQ}97b`Bz={H6VyMqG%Tl`+peZ4^zy}pWi-$ zd>lJ4K3UuKj#=zH4+f8|Z>7&*C7d4Veb+k#24mwF{ZBj%kD_CV$T;cO*#F<0*35=g z+D6!7Kq+4*&-3d4e79873a%A80@>{w5%#OSPA@3GpWpx{Y@#4Qz7p99)a^X!$;F5>}xV*zgb!gSYc3w>_eslK~w+k-g(B|CF=W4lAR`Yoc_pAXTTf)tybv25} z#;zY{`#=yNHOHIJcijq`^S?qlk6w$XrXWCT>=xQH^a!3V;N*W7{4MMth=_#ObrPZ~ zwqswTgKJ;0#z&MsU_m_Mbzj%L6ZX!#7CpMs8d?S)8h$qmx0L+F8i5HYlCytv?xvRbAU^X#ON-n8XdxBq;8+$nQ78th}?ahf}z9e=H%CH%)h&~@UQgwwpXPEwOvjc zYzTA)0wC>^0G|cOQRA>uqcF~YV-A-#d_FGJ{~42JZ;5M5T=sSW5Vh}OQ#wicKmc!Q z^tI>JQ;8ORNr$RCo-yXJeQOgn`Rh_xw8Q5gqX6e9C4Y|k3A-7r=CXxJuX*^RJ-3zD z%|Lt?l#;B6Jt^V&U$NTdF8-LgO#HsEl&hgH*O^PfP-{)QsOFr>M#O{!L5Vm3MdLnp zh0g%?%WEaHJEPYondqE-u}ep?=DeibtMs&JZQR8)_0C2a_#g<2Kg;?eSD5_-jBJiN zXpMtrj8I|)9#2Bck59w_SFypfxDevAJ(*G6a*J|wZ02c2)W#VkrqE*?+UEVJj=JI3 zF+(N{1VPC?OV*6+?|jitvy;l;I-OM&P)qiUAvfF1hNQz4ZCpNXtGEnPTbKX^4SyDQ ztlcv(cBbGfJlcEChFinNkWrtbt$H0LkfAT8J*KGWHW{qVFi^@)lQ5t1#k-Z3&Fg;i zcVppM*J_xuCn$1Rwj&k6(VT?5??D0)ZPJxS!{{*7I4S6$KefE|H=ZiwhpVC5Wx~UN z9!b(qK)OtQMYqKKC2%*2?GVZv#N9`tVBrze0wUx(qI8t1^dtxZkV!Hw*Z>Fu2zbbk zh*%~O1dSY1Qwpc=d77McUJ#OEE_Z!r?%3`)ZBp;reY?x+pzziHD%6NV zJ0v7Rn~D$_)h67GXPA1ODc{HUDE7Z`BpiV#teO>{Q$fF$XcWi*5HN^uL4<(71BO@h z<9IYYqxNwy-wuzNo(;YMj-#a$9rN8F_Sq2;0l${`011u!@dxMq%JZE;Cs}^g@U4-P zFC7gWV{zFsIq$?vk2}Dz(ovFQmDcg2S_k2K8>;T-Pw4X4c|8~3)_>yc9P@acjEDdV zoD*AfMr*@44NQEQjbZdbnl(?;hcY);&vyp@QA?Y@&8Y>`@csG?5ZD$z+m(*S+E7um z$oCjz)8SRrDb{MiE;Uf3lA!2x(r>bQ(XQvf0tL9P$?AhJ@0V3<@=ZF1&7LiJ;pv5y z@IbRWRNWrKlYeqy4)a%$9j8TUT1SO4L?xsS*MmR#L8;K!a;Zfxq>FDh zKmbJFfa6}z0k|S#roCb!wMy4GNP!Sfop?U32O~_n@W{s5&W|DlFO=Zqj?aGGX!N$U zE)Nw@^OB!7j)cl!MBX6D=30EqXnu%x!Ha8qtd3jI!r!m!-QTKo)T+C(H#hGGZ=YeW zZs)#B$HCh#v{d_0Se?;A+V}Q33h2-P3dEFNi1#+8w%-{X)tPfZHSGF3p+jw^s}Vf1 z(#p`MTvHJ$*70|`iM8ugE4WE@1}7S{o7n9kYUQFI;4OiBBu6h2^>DvS;^~ScfHSs_g;6USbk77INECXaxJ=c z`Z2Y+kyY@@h46dwqoc%@OGQ5uUhviEk_0(aVHX!}#x0!bQLy0T0pn+i4a(0VW z54ikI`%*8f>cLap?;_CF{kAmyji%A;Qa3yTc>n?;3dinG2WjehIXRwY@r`qu0TBx1 z>&H$$hvjm8&wQcF;4jEhIN4~}dNr6`)Sq)tv!urv)BNk4rERnDxe5vl?9c1yt zNGMFd5or7!3%?kX)0NoNMq$v{`c*6lh!H`bO$^}>AYe|dZJ1MIYoJl<#E#wK|7y4CoN+);yaw%dCo?e0?7h>90+pgM!sl?oWZqm>^S+DyeDwmDF$+tf?W}z#;#)v}~%+a*#^y6^IKH1Oa^?~gVy#kN%w}W8JGxM6T>rVeosod}P^dMl`p3{@QLFtfzr^WrG3uz#pY4Rn=0JT=XV7q;;y7mi znHD`&b>HjVgyO0%^78;91(bT96vN5vkr5G~Z(Nao)xvd`IsVkPb}f1JA8)Re!^Uqt zxreWUpzN-FZ1tDCtNE&lyZ&uYTBhc2l=qR+kMTWIIqJJtXBzSpKq4XXW0J@|H*?T7 zh3wC4lSl2@24&V@YDwO z$bAMwN6vVx9ObnazU`M$MJpE~^i;Y*E}|#@j*ZPcdS;Kiv6IgUb0(|bPSZ5jewX8cTlzTNKg59tec*NndF;l!=KNaCn@-=(0+V9PWlpHK$gr!$g=Nh zX;pt2gsq>!+$6cjoJd-L9jPV%&yNJszRQ`b<=6rM0zWVtGw&jA)2Iz1BEVJU?jb<{)PDCdiHdm}9Q#{)yg&OwYd; z&i$ftYXSYUS+soVr%`l!8jG~I>XjuLf&_h@<=xC=`$yo6AV7e^f*q=Wh;9mtzTav^ zPw)V#1w}oTMLe3in#5CN01S8nDMe9oeP#XL_UoTlsoQWKy^jl^y>6UtV_nf|F#ATO z#a^TcWKu&bN(55@?~qX>9PaRmILgZ%R`*cYs6;(Ww(qLzIVSxAK8UQU&W$r+L&A&^Hit9xc# ziF?)GhO5pxZWL*qxUS32>ulVY2}s@bK$q6<*Lxb}+`tFjd-3RxjeF4IJQ^5Yu)@=T z2fqnZIR_i60T62IRB4i)zyt`6>hsfdyQU(6eP^`=o06Y(~(<5ek}4Q(3hcRli6 zlIJJP?sR*z=KN=3Liu5NfI-&lEx7(Z%J<>1ULOAC%n$`ZaX0$&LF2nh(9~}LA8wmq z0q<);Nyg_(b*oY~Es7e%!MbZx$cIaMy0YoWMjP^SBKDvukRU_>p*$K;17yab)6==L zo#$2QOJ>)FC>(k)Rk(_vZ3Y=tN8o~ro1_jqvia)Y`b-{junmtN#r-}$cHBs{=d1=q z$m|e2$DHlsC9%C1V(IXWGq8XFY^kTsBjAuAHC+E`U2qr6et^-T!mKwK8!z$w_MF`4 zSn*64{k1qi^0w^!Si2eFJPZOL>czWcbv)zIO~Yz6G>Y-!c<}nW4%8%vN=A)-uxoVi|EjB|yd|k+bZ-Lkx|cu8rHR^a z8f0l>bZLZQ%qlvk=k)ws7CwI=inxI8^zS~+=KW*|>=nI0k+J1inQ^)vt0%Lp&L2jt zMwvD;qkA#G(EtGRH!YvRMbk8G<(td4#vJ+6i4^a}`D%$Pcaqh!#TQ?gl zL{JNnMj4Qt3B{(zC4D%?jXF!_bO9!U4!PR}+{L-Q zZA5}pJ&CoyR?!ddIl$IS;7r^Zl^*P;*LkQJe$UCkKoAc4NWvl_kz;QDIOMjd z#WwM3Qby&dHs^hZl&ELCIaNP+IO|Pw8GN1tk5yl+`Mgo~qYXfPLM7aiUtEh?1M5UXaoBIg>FuvkXu#tbZTKGK7P%FCOupymR==m* z-pOrTZ&-Xwp84-xZkpaNN5j#e^6$`p4rc+^KEj<3z@Y$$K>1ux*AW`@TgiABGq>?M zo3Tp#Si4ukOFrkIG|~}tc5 zeAesIYVir$kQjgI7J87l>-%}5s&-WscXDkNtT^c z^gGt#J$vH1bbUh8_e+zLcxKR8XlHOvt)(njD7B5f>(@5d`=X^>_b(vpQr;w86Kc#gUsJ}0Ly>b! z~dCq){K#>m5S&!RvzUFiB{5L%gUMkZRW7ttw0C-7aNN@2oB~G7Jr|XjSm{94uX`2man+ zrLL7MU2jUC9+o({%S^npm2mmK9<#a6Y2VjVS7PYl)WZ@A47s`Nv zuhk|7V*+Y{YEi>hnefzl`ug{WR1-&_lH}O*l{TKfR!0DeK6=L>@hpSXwMDs62IEt|%K(tN95f1+W`Mysz@?7<=U2>CJZd*SU>bOPV&XEuxL97q}7ytwa zgZ1;vgMTL%IVj3~Lf*ps_Yp%-zwgcZ!jQ)R5dLav8lOKkC9>RTlv=AGw7Y03)&=--d+cU-H6y1hLW=Z$F<i4^uO*56KKcB;2;2#K`Fc_Pr zD*b}X12!g@_21i^$nBe-K+uaKcJKC%CJ!faK@E2l6=&2X>I5?4`pQF*GlsF$nP!NR4>D7>g?V25ipUdN++#ZcYov% zjRMQ6WNr6cemyO+CFtpDsk`(l=>F)f6KS2?MmJ@Ls`j352hW>`+Tg|UI5O>35XNRK zO-4g1W5sB9Is1o=7MF?DMU&7R+dj9&=-;gn_K+J67Mt~Hu%@Dni{(Hp3Dig&?)4(Y zwe`>luqel#?uz>IE-xciTjXVV>I`=($<`UAy{(+6tl& zc+;$sL#7T-gE^Xaw}D_mE4|3l{nUf*Pk++q;NpmZ4k#N4&i|;%Di6*0edPt^l~Cn6 zz4R;Iz3$0ZL3-2dzR{J<>>4i59SrlY{~#BNbi8cvc_^fqVFI5z@2rab7PxSRK!87u zJ+h#|-}7~ju5&s>g{I+U?V1_bXVjwCC!E+>Ps-?z{T3}uK0Y9j`u4dReQ}G1X|R`K zXjC0mq4a+$j^uqA&4dU2Ba*e>Jaq4=M_jqk;Zn6?a&tLIbjC1zu!2rav@%6cwf?HN%Wmaw3p2qgG z;jO22LbQ^jGc=X0$#;F7VP5C7Wjnok&VN@?Zl`)>=g%AAgNpXTOl{i`;JTmkg=>N} zJG0czKP@>m_Ir1}X5*40w{bYwvHe5swROw*l`*Wc-~HY`d=-ld(=d3(>+uh^#Wf(zUA-uuklgO zF-PI@r|K$R|3;(36a?k>-Aj~VUM};~Z#CnmXPV))zDf)ASDpYxFfYC8grVS=VQ`>} zwszjJxaPR;>9KidpJo0`BaG^w_s-NU)mFbG<}T(rL7N=dc-K$q$}8HGAzM0@M6$GR zF{IIX>@)1lYxnm}(ES!~(eB{IB}E*5<$T=7RUC+~UA#WqbSB&MO1XvZ#S1511x*S9xfj%J+)k~ zH-HOotrsKlhX%5%1PE3gvHLv{0u~VipeUvE!u4~}tFJ`iMV?uZUiEdBlm3%wYt`-k zTqzg*-M-tOJ^&1jSrIJF5~)ZdSF^knjInm##HB zgaF0+>xU0&+Pr`ep(m_*jm`S^F(}zFIk#u#wq0Daz{AP9yL5dcc5 zGpj$h#>F6kZ}IzaDt5KenWczyeZL};RGMy#77??l)4Tdf&c5XDh0S^0DXv?hxrg}p zhw9CW{3DysotLW$d3)Jm-(}-3sq>q=8=UViY~Xaa+u3LW!!)I>Fv7CQA*!S20wN$p z%m@%SXSG@MF84HZDBuBj02=HlR$Rx6>B9A^Im7!mIb2I98jn=Za^nvz0e3v7w`=V{ z>skPhFcUA6{uMmKmAK0DnfIk077mL`hHU!QyUah;L_Z!n9$^Wk?dy7eb+>x_%>U*Jxo zS0N+)Th)7aZF5~*Z`(<=(tD)=w95m5m3L9%sycGixZLrOt2kOcoMd0^^$&>!|9|2a zH8q%B}XhgW48ceg@0GbN~SIPoF{BT5mA<_RH}mCfTw+Ai{5W zoUAgi=QZ)64jT@2%{ofC&sz3ac^D+IvhEUZJ!dM^f8J@k zD96e92vA;iKDLHQqyE$<{}s6A@nX~w`yUC8fqu=*+TPK-QzI30`_0qc}w9%3P z0u6(eZ4Pe!lB4^QH&^0rrX^K*zErqO`j2Mwrh8U!ZxVdl(oadogG}CiV761rF)pvz z%~W05cZi#dj{|7K;14ODIQ5CS)l0Zlm4B=G`cpXbEMc^TgkWv z-WhJ|W!0u7E_sV7>&$PQo`D%f_;Zb!&~WjNsn6K3|Ebwo6fV3xm-jes$=(;g#dXd) z;VJ)$k*=$j4qV062kTDaUV$h*cYvkY<>6TM$Px8fxoCU~U?;}M-m=RZIaWRzN&F+0 zc1IV|{{3R#001#89i3*8?De_`jZKSFU)Il0z{0bw-)J zecwT(@C71_ekK@tI@t~pD`L+(nT5_+0zkk7HyRTqLSP9q0Ro@2KWs3~EM3o@;ftB$ zJ$Gi3Z<_q-v>)ZvI^-Om8K23IR^@%Ag~2vqV46P&CdJO8!fW_{YiB7|k4H_vN2G+N zJhMCz-8MAlZab)GE9cp6z#<-~c-(&{AogN+{yb-4bvG%Kg#yKiSY>2U@oevH8YMa! zS*7CDzmc^?}8ZdvpokWs!-H< zN(Bl~C{lq!lnNB61ytVq?b9`Y)ObHVgZX_L$Nb{+`|khz?T`f%REnr7zMog}2Y1S- zstGMjXAMVJ<1HCAw>7il?qC-)^muBXe^aTOS#5@aSe@g4PI%58RRQDtR~^{~vx?Rj zy<$>$9qFV#rmrq$G4J#4Y%|qZfXa_e^w>JP_%FY!ntyNoe;v7ERL*e_dH1ruY+JtF z?&|vz8Iff_AK}HG)5!s$t~!~>qgP~S5}hi|-`{-z=?A^i9qn1sbvyo~aj*al>eQo! zv)8>`vrzbPebT;MwGBV1ax#9o0rhu(+V(|p)BdU41no(o0wN+gEpyl1J%7hDTZh-I zl19O8m+k715)rlW)hl?GA6Mao|G8CVdfXl&gb*TrPm=lI)l_OxI8M;Zi=RUg631Lz zKne8)(LuD!oo&SoDsm4q-PzL^)}A9&`uY?}!A}yN+1dTSPL2Q~-a2qUlpA!}bDlVy z)XMGVJv58|8))mb!Jsg-?>G0`sWz!ZZ1Th16yW?$WuODZEXTg$4f4! zgV_K9ROf`#XCi!)${VClFAD8VjHlK;hLR}v})jrc_U*kJ}%nT%R zkNl11M>5R2Y2vE&px7%L3OY;XrT@+;nIYZou4tmQ49D^6n$=-5JJJOJ2!spl(bn7D z#Udgi1S$HAUjbi?&~Mp%VIm*~q*UWruyKn5rMG4&gfoVL#=MSiGY(&61vs`&J z)dgG6N<^%9LPB^=c)&W&j+MjWD!s5QOYY9i5>6n_H4zXZd^%AgA`++3U5)`@x!l_1 z@w-^4hv>8q6n&;8$%u}eAUX!ReflG?pDu&OJ|04}_?tS3G@pY-Rp^z8Z4{Q;mWc1a zq0j&ZgK&?(%Hl~uv8y4vhE*QYi=U&ElJ4nluW1i#b74IX18vmeR(U(E9s4#+#=egC z$z+%0|J5!}bFfJ=b>s0giVC&56UV}^o`SD61u3sI`&QuXfo2Cx!hZA2kWc9P5<}Cs z_GQ1R_%(G8C)s)Wa-pL@1Vk>G^XKVc=x5Cv$+@?}l6YA4Lw@`vyGZ`YuJbB$NF4Z8 zj2HCu7uf|4(GUb&IKD-TBYF9s`Dn?I*@<82j6q(P+C#f}aF?uOaNcXHls2hMPtIcOoJA6uJ)OhCZ>yHhWTi2&RQWf1-~k?{S%}baWJ&^QpvJ z`g4|g4Nev3Wp}ei-M~#(F5^~;gVUuo`GnF(tRZeO@3VPZ$F$Q}{)ZTo& zjo6QD>QY7mzo{kK6OiiEeeL5*B!xtI z8@NYfm8?&{5CQ;d!3zQah`|Ws2^xgOKw~f7Qb>{Dr!WyCeBr+yHu#Azc|7|Ig^Zv0 z5fKmrJehxgPbO_tm!|0N?)+I$qhz3UUA{`_ym1&>LZ0VuzN{9JV?QN-rU;{YQ?%%PFv{fV5ishP-XQR`dW)w#n#o6!un^&Po&Nu}c@`4A*lRc6PKD&dI|KbdocU zu;~TK=r_yFJ};i4dU(g6kMyr8$zS>`ACpY6dChl~;`G53+i`YZZT3bp9|#uR4TDmntNN$por`LaKmoiWAO+io zy|LbNm3l_PTLwUZ5fA|kMB)LUAV7*QyLH6&5%esw1y5p%`}yNAPvr znBMD|(|$4sIE}b%v<7~d(FeI>fNeDL2xGB1{|pM`G!I`KmAXDC0t7`bqVh<1Jtwuj zR+Mq1&IgFdy%X-S{2dXY+xbCY8sxqC4Uy3FNj9p}rv~;&)vHg{Ly6?;{N_91`meM2 zZut&(>Q#L?2enP-kCW1)`JoV?bV?Eu5!L| zOKU!BA@TIw&L?>#A9e7G>xwrGX%QbR@eRU`e~6y%T@uLuAK z4XXu2>=^W5MX@+Gxb{Z!(Y$D959jz%{kj9Pn!fcyuAmtu==(7@f(1Q7!iIiL@cD+) zz?z7FA|OOWUN47b_S&!a-UMDe0MI}$zk>Kj|I-##ww4}*NlAk2zpJWtWH8Bx@H}eJ$bC5iYKP+;+*)tv6_j{Ei$aDetz9V zXDHfhe!PqPkPYcOmG|lgid}V1?CV;rOql=>1dBWX28ThEzo-0)sfYE*4Z2~Qsh#hl zZvO(b<)bnK8=B?`D8YaMg9HjeqZkyCAp$ahMl_HbLXJzh>0cwq+^)4kK5SPM{ZvMj zqaz{`l7N-Dj~a>Y2?%JWVH|=I)#ZLgsa!CnkAfl%9H*!w%a?yS^=F*Ty%EMO zZ-pbQ^2edHmI*sTZxVE`l{g8JJ1K)k#nIbjpI_n_9LxYj7(iYD10%tPU?^%Z^!+I0 zbG^P?2j$1)U^uu?BN*K_<7hxU4Jr@9gF{n;nlr(IrvIvEg8Cdsr^X#?Gg1KpF7nYW zG^LK`iNxoVs^KP;7*1!(k*3JG*=FP=Om?a$F*#kn(&TDsLRv&BF*(5GVM8u+-uAL+ zu!zWEoDmd*``Gn2w)V%wC;YDz9#3uO$RuF)+8I-V!Pbjev;l z;`wdC{=u@hCZUtEJQV2V$6MQwA|e1#={?>5qu@Aw#z_ezo^HA1^IozlfpM_lyB?=N zzv|nTnmiibUh?rCXS_)k6Gu;OhDx!SvDBKazW&uQ|_@ zm&l~=pd&BeD#Uy{?km}{hph;zg6y41kX zC*+@N_m>oj{cabT%e6Y4ra?b)Ro#4Q6>>ChNlH@QPEwYNk0kPEhSN*5(#UbJR6k9e zI?YA}KT}E(B{L5cY~FIc=5@MG_g}}sptMz@YKqBJAPBT@S=HbG2N%Z~F`bJ~Em?-; zrm9pO2JyM~uj1kCZ&tTms}JmTafE-CTHww0xUT)y&7d-K2rYFjfEOJFAgh(*KCahm zQ=i~d3%K)tR2i{mT64qG2>CK#5B_DmKpQ`x}lBYdzHduM3fhBAbKB>W> zdm}!!FxOthXB){V zozC~!K?7o-oYgnIpJY~5<1QWjot%~idGbz)Bo_PtK(f!)D!UcF-iC#%pEmxt6W?i5 zfQNOR>>zV!dWdO0UkCsPW6g=Mug~$U^cmz8A4UeLn>O8IlOuiZ!Ep4Ub)hd|o!u#dspL>XowLJ0zwHXsf=fo2M@C6|EF@tJN$f<{q@He(C$2X7;72Vxbuep6?S~4 zdi<4@oA>nruBsI1#mm1YJnu`!*IGK^G|{cVh<79nb(HRg-~uirDwhIn7dTseJ$<_B zM(VUorHp1hs%fjsMjFAVXWjggF`AtHc#-l3B=-#v?5rRW0MjA7@ho2UCce4L(#V-z z+yl*LI(PsAx|C1Hj`hC%r!D39JATUTz0U)W`I@z-E9h?;3f!(3L(GT^aetu=R*4_$ zY4rV8t-XDi%Z>k^BqU9iZQlK7#o|)#EVeWbN0jjmbGllTUr*PuD|J}i{Q4+Uq0J;9 zd^)P|K!^emLe(v~A(ZXTf6*Zuu*gW$MBh#BK>{6Lu9FXcZ6DjBW_6Hi9A4L^W9Xd( zX8m@^?jr^F&mR$6%Rs<-Me5-vc!2^0MaVHB`};9e2#A241~prc14CrDbSN}(@+kO- z8l6u0Q)3v&d&ukExKO~X>k`|z0jnJjwB7aT84V^d;Vm$LA;7`zS^xl+MS$BNz8-D} z0}w$Vs9;72p$R0CNgxOyQAJQxwSuZ5o>@prG$}%bC{U#u6r)0vWC0Wsg&@dCN(iEv z$UcxTGV;uV5}mtOw7*Zow{^ddGob%PuNZHfw$=(^p#TvA4FHH^=UrrkkYh=GQ~>~A zf+8SAg`)=KHe{V&KwQ2$wrE~b&h1&CAH%KQ$^h>1~JfaQQ%h0|K|eiz25ZsUQ>-|f_RpC+ifUAF`RA_0f=;Aj9Pj$hg8qMKxYH2q`B zlwi;Fm!=)SA{uD)oc}4`jfKjx(jf#MIS~V0$(t|hWvKq7_pWP~6n-@O2-|6&&E7cu zx2Bo}OT+3cp~a905E9c($XwYiR+69HU$I@hAVybU;m)0Wy(qH$#gQJ+n4=CUI2?#- zjlAN|DnSY{9MYFUfeO#O@GRYP7v1gu+uonbiSegzH*eD-0E#rA811AtfrI&b|2_Z! z#k@vi(%t=HQW3Nt$S%OoxPyhz&zYK_Er-qnAMLzyjh1a zAb4x(-^o{WbXxBDZ{AQcjl1H0Y0i!G%|BDQT^*emVfM8d%ndgFEeg)rBzob>N&|^9 z9gG%Yt6Xm4HBaSZLw$zlFl=vpUv)SDh!AKxr$yo3bpQZEspFu>pis|kZH>y(V)Q%| zGQ=qsA8I=Z0Ei8~&SR+X9B-n_=+p>@sjL7+DB-AIA|OOQ8Exr5WBzWM{L;44)ot?yVGjl19ba^KcX13Q7{(jzn>j~?$5Wux8%AK!aIC7l0wS8^~BiXzlU z=IM@X9HqBt+_v#@3bg&;qWHO>Nd2N)`?LmUA^<-b%d-Hfy9(n{5h@^%BNbHK2@nH zd+T)vIi9O!vq_vmyMylZ2#A1E3H4+^h^qAc6=^i-O?$abUC%0@p9AwoSMrsJh=4l` z6$=V>aQ)O5d(a4o^nRMm8WNEBKPcZcF{IX+*}U&v$J{?3+^w_rPVOD{&`jzcv>xKP z`2um)X`7bdy{$T$9=(FC(8o-*NlR?eJ1FOwrncvZ zZx8Q9wZwbiVd8nqhMc6P0RkdkSb$Sis58i_lQkkD0k8KMnwNS1ST8DnM_<(i?NWuT zZ9+5Rb9@#46eOf4Q|RKrzIy1MJ*ol#cdJjmUsywEQM)V1qC_5YGH&4my_89z32Dok zarb6+j%F(sR^??SJoCo(k&jM*cV?1;eSeKCk45*-c=sfTtTJ_~&t^>^j|$|!2F&Ou zSRm)0wn}GSejgLi?WJWm*tVfvK1W0`4oSiy4Hw*YMP^|P`eu1P{wXnIwf^Kt|6vfp z5d^Jmao%)zx|&-*M)T=g;_sawZIJvcd)v_m?6jr=@ggife^#teD;=gCTtv2je33;A zM>TW#beGP*p8JzkuHnff~T7%vX-b)&f3^AY7dy z{e@Bw1c-<^_{Sjp7s;ps0CEv~`Lz0|XMd%|(suh3>+5h(zeIIU)+F!qeg^%MvQ zw&rYU=%bcz87FBSu1`U)yJ4IG5cfN-r_toQYO3RSs;V$P$D4cQZS5-fvNkLGpa`YE z-t+qcwNY^FDl!T_T7E9PA=uTYGV-9^J2V>^zx5BT{&KlT9q}ub4*7X+?$}m9>2cSt zx|O=0MyL=X8W3KGM`}U+uU~WhuX=s3A44XCb^7Mxd!*ML8NWQ}v$}U4`#C-dDHIp` z2U^9pyfshmHG`*@h>gXK9vva^5$<01v;8GD<{NIYJB0)Q1VM^ND1ukBK~XMyJj~wP zkerQ8o%AhSTu>f?z@Wo^(koy6J_p;%Du7#X68Wk;-6biNBtVN?du!D78Yn>MtebQ( zL;(UO{H+WYy!6shPVR!5#yV9O%VWB-(r(%nG|P9NaZ*G?Q4)@%-+HKZ{okN>o?D%M zFk1fP{R+Ab5CDM`R#tf07{f=~!1~$y#C(rVqdufEOu zaR7)0N?f6}45QHI++!wX)KE21M#cul_6OYAaZ1euk z>&SE4G3yt>nZ^q7e&#dwR0 zEUAp`L2v6s02p*S9q%H1+IhMhIS~;`OP-cFLly3d7`yj_UriiD2m|Wo^5>Wi@3>Z# zzzhiHz9wdfz@l=~el%({B-XZkww{&s-tDJs%)WNv&OB|!`1cw=ZXNv;u7hgtJUxM44FD; zR}#}lPEf{fv}<(LSFc_-H9s|#J$U!O*^JL&1CUmYd-!b%sp)D+kXP!firg##;+&jp*uMPe12MDHg!WK ze@bIde*;Row;Y z>(vGw&k`+F82D+eB$UY`$)nhxH-G?lerq?xmjdggZdtC8e?6GgNZH74PpX-JcQ$)9 z|02ZL61<7;t5e305MD9^E&u?CxRm*=es+S-$+1tr)a?u5F*K9bU3;QR2r#MvZvIef(tC@ z%HKr)2CiRt$(ex;IHujQyjmuW<|666&a+SO`9@`CeQhtLcdgVb@pPN5{;UuvAHw8% zQNng*&hORdtp9-k0-wR&3%wGNI`jX!ZGzJ&b9}3-hi90WGVNnOE8JJI9eR9RpacMO zzDdrpcXp=t^xtQ{f^fr9q=@x1F({mRj_rrl@5qM|00CEPJe`2kyF-@EGGD(Nwb3Sz zm-?NV@^TOU=~7)U`~U!RV5B?@iX=n;g@8@&lmx{n=(~%K#&?pd#v5g&LpKqJf?@~fo5;S<7Z93bN$9V>^5fB*z z_(#+Pxk-VK;?$!AS`4YXo_TJ2(@a+^Q+YZQtXKBA)f^b)A6q$0TBbJmwgGsLJ5$?q za7R;VKfZ30a9)bzYh8gs?ftYlJa6S*&*ERAwXo*r-wjcl%k(`dzV2yYO_Q_R;DaxK%4+XiqD*Z8+CjNYP z+WYvoxN7m8iE)AeAV&u&=E@i_y>bKqc4+(4{~jgTh!7W)eCgx8)kd*`YZ1_| zK#Bl?6M4`9w@%<~S6h{^mOnft$Br5N;)2W%I-kgnBxcd9eTHyyw?1wp%EYG@0||#W z0rH4ZbwjL_cyU)nh=FN!zwC~Dkeq<>z<=lsWm*x9SGlP2A8UE!o8fr5=r!w8RGH$% zx?sgoA_DZlF=Mh#f-1RV(>1Gzw#V0-UGg7dc_lI%O!?N@))p5DOd|ys)2Q#fSVsW+ z^cD>jEP#pza}&t9@YgriyFRP2AVe-l*ZD_Iga89J8Wo+C}#Tk*a(TncYsGt*+r#xxkJK#vf;Df?NLv1e-k){5^PnR5zP18$`hOv%0CtIyN*A9U0#Ao z$J>=Z_gkBUbG%xHsh0ZouNt!#pY68@KmasJxmnXAE7z_QYxmkr9R=NXKe@a6q-~V* zj;wl$in^ScM_I%V@JoNX4;N$lAvkis0P*RkN6>KHaxKxiRu=v$ubJqSm%aU#8 zttEsNtTXRgKkNYlB60L6p{n#nc*p`GFK)6zV8t4?>-Ab2T+k%n>2Fd#Z2H|7O~a!# zsHJ4X&)Zc10Esu{udrwMOZ%>wn4+Fnvyw&Pn4=rw&s4hXYwJPssmp^)Tq512Dqax5}xR&1m;M9Ns?nyV-eiePMJ+OZ(f15M{A_*6=-~brUUx>)g+H) z*P@7f+vxcoXC43RmFIkGt5fXb;z;R^=@0prJa+5!a(264llmfZ^>){{&%1RA8=u#6 zt8;M#N5~{XLi~d4aG;1#L?|K@5ef)|2tW`+BqSsxC4Q$**y(h|-CHKn{HbD~1OY_P z5C8%KPgV%H*9$NYb(?+U+dEfdv}>G7*M9q&g|1DEY#>YXXLz?U)saF2E2G`{czAH| z2=;3Bd+a%NXxH`aqcQf0u-3zRwPVKpr%blWP-rDp{L4Ll5=I%w#}_h!>XK#E5_v9Q)~(SsO%r zP9?+Fm}(hW2IK4|MqwwRrC>Ny72XG}@A`WqOq1Ke_e~=(cYzTD8W|EgxgO}n)BV(` z?8&Ko?{1?z-R1w-%K+|HeE8P0;SK=uAv;mX2`x1?myyBzG!AhMs$L1E(c_=g#s@qsE~`}iLoL=CuVJmoZC(w# z_?$1yyxo+jTxnYFdfypmUzkECw0zGp2BJzY zZBo%euaTkgq`f9enRnQ(j%X!Qxf52A^Tmw$akfvH8Ni8l?-PP% zS65r)#cNt_s(n!U)O+ZJtK&K9cxg@3B=3J8m(DXQOWbnPeNu9FyIJ(L5Pkvx8eiMv z`W*ifk_RF0X*D=PPPXNncmAEsi6Gd$zVnmbPMvxDbs*(WH*l`9k<*N@{vKTIPDz2I zHMg5Lee$%+A2+?=3wYn^W=+_N|YejpH{AsqyRLg5}WE^R)CiTm~5Wk;Vi& z3?$QeRY!uDsRr$5rJAn2$fxyxR&a0T+T1F;{mwzex%r=^+QQ;;Q%8u;Mp@rX^@&4O zmF?w+CpRQ+izTiUSCH}&HHNvm(me|EdHG9|FS0s!Yfql2#|-@Qr1=6?0L!fb*|8ae zLCz_paOnj`5e@N#LsxI=+R}Fr^O+HK@OYM_0#}wDvd_*;4|Q3?){=Q~0YwC1(YRQu zl#t+r1}Pg}bOSMm-Sr~T+sds$qP2&*#i7xc%z>@HpXPF!!3<|)DB=K!fFU7R(2-&O z1vV-nov&Y1x53WA1p%wuw(DG>$QooC@_qx`YDD~ru5I&=ll>_KK;A!P=TDWw1&|d{ z1lr)^O?CpIL-G5Zf8x-FfV2ps%sr}-6V~%|aEL9OJi#4T7GDi=I5k%OXyNu9 zNW+5q=2F)wSCyfUd;oxGD{Of*{QqY2rQt0w6FH#obVnIuGqK+4>5u>-2WTPQopnv& zt*2)xiU9&ES>2dFh+A{tN{aofNBeG*1i3MEul?T9wDYFF%IRSE(hu!FSTY1eKn;#} zd%V+C;VwjzRrlGVtDqEKZL*f&&eX&e(QD2hqy~Do20$Jpm&Rlj)bHqFbT96ky8;A6 zKnRJ|n-ldJ*I7p@ULvF4y@)u5a;nTtINVahAod|kb z0H?sq{V)Od0Kt>89P1b#yizNLb06t4<01stR z5C!;YJn&YARAfpu8crr%L_W~*SwrrZzF|9~PfFd)WL6%X=4V$V5YG6Kri^f4@}x>I zbr=ws)tOdx7I(uJo*+PoiFj)n7?txGwjN_hh=Bktfa&IXnEH_czfx?9<}G|PynAnX z0wN#?RKuq&$?HwFvTfjh_0l!DtS)Lo-|vdl?O)K?eU~GqP=6@h8K0g4D~rdwE}OjT zIH1c%%Sf2Zc2J(N(oX;dGMogw|0hA-9*h>L)vIC2&Wun%4e$0xmirZp9$=H`4U8K; z$&>#d;&~2n_gcwHx@PdDS0^(H@s$(^f`{ehzfAb$0slE-Kjm3|XF~f}ySG7}+-;#*xUji?vr{pJ?D%=+p{A{kR3-ofOfa6ljKV5C z4Z9A1t<(QZP{|&5oNB%&U=aZUck|qEwpcLs2L*5dR)7SEUi}1bCvR!~pl;9TK-G9Xm4G}_ZmHx$mS`PTeFOH!(WZr!>`z~EYf5jRyK{eA{zC;K!}KntU*v4 zsPv7Dh5{K7AYaS^bGF$xf+8Vh*w*YmiF_B7&pg7z2F0S4SLL8&!RjisdX{9{RoylC zzODbk=@!Nt=`}e(z_qQ^Rb;}?>ST7%h`P`KDL6fm`J4H?&rJ!);imKROD{<%2D@lh zUT2EkVj5ZyHRGg9E%hKEL*u+Rgu^~`+ zF7EWco_!XRcHr=v!$VhU#_mc^s;q6(u>b%|$n34**Xr!8OTa~Z20&nan0r+cUGd3}!{BC5>A5hHU4)%rjCk@R=%m#EMG zpL1)y?{_nx2pTtji56P}XF%X2i8!2HuL+OydEyJMcE19Hz2d&zt6ZK&gWTq|O^0LA z{Sq>99I-b!{_eOD5M4fu*>UM4F=%P(jvv(}uy3>ui&<1G{=SF=L%xND-7h)!n*kp8IQ=sNgf_O`81ZMvtLXP&M%JjBzlS!x6bt#1dS zCfJbIr$rRGGsw<(p1=eMw^|-kFaQx2LNX^*c!-D*J61|I2L=cAo?lxk00QPQf7NS) zxmJnBw|*$flR)`bWzCzt!jLStmyCuKW(+z35C%A1tfW<>bbrTjoequIDEv;aX=)Ry%43726<)805 zGLPS+;rjmirm0=w;b|JYjasyAit9~5wD3eh-%x|{S3cVLzyMW9DOFmOik_%|AVt2I zN5R2v(OTOASf((~kVHgB0D<{}7=0rki>hG0CJ1G^Q7a0A?S}TK-g2u?WdgfnOW5}! z&g_&dw*zb&Kha%wX$eI?NQzOG?%1AE+w|+(V)?7KI|L6-ab}~e>!p^{ufWad*5m7$ zp7?j^xrmSL(&LIfe02;OZYAw+jbrLSh<%r=y;cb7QpTz|_FG&dtOZ~v2hLmL%ijs&xnFTty* zsX5q0xcA*h9t;>WZEcg$%HV(k4&`MsIlr|DWXU#wXU)3#`_10%aJE50C9&6XB9Qz zJJNHx`{k@Dd97x(!+-Mt0V!XKi^^H($dB^aFc@t4V2U0&tUNGM7c`jpB^_zV;8yL;)A(hnpR`Uazw{J+vuZv*6xHjI9W zn0N@jI^8JXlIT+!qEkOxzIbKXJHChvxlR(#U+D(#^`WFv;(Vzz`x@i_MYjJh(b;zL z1Jn)utOZ{?&l}~B)ao6+aPOH(vD65L^Q~t3A(fc__WzY?%=gONd+>ffV^#n8ddA&2 zd6m9W&v1>t{HuOQ(M0$IJ+p0(izEAO)MGO})a`$X{-+FW_UuP+%UZ&aFE<{bD*K@R znKCn8TlS5iMRtJzL{l6rBqT76i~8Sx(j-&*_9vKM(cCKf$0=9+4O{NEi{U)w|2|fn zdFpOs zrK@;oR3-vUeWlce>BzbVxL`XAw5EZZ+^^r`r156`osUxNdJdw+dP=$@(aloy&q6m! zPqD6dW!kD*wHV4)s~T-D0&lFZhc394#Gmo@Wc*2{uSW4!N1|;pc31kOh&{gI*U2@I(h^+UH5}tl#s5ul_~Xq*=*2Oe?gS&q1&2qRv`XbhsIo8jR-}lKRmn z7~*X|wwp$$B(%2;J!N^1VYQoNX+)0#ah2iPt=a>StT;cXV(0Z4OCn}q^cqS5x50Jq z*_^0PCJ26g{)z*5VFKz)I;UvY$I)PFxs8{dG!E)5>)L&yA2grIwRT*SxSOzb`b_Kg znh6%bTNa$hu&WZPkkQffzJqq1Z|EWbO<+@}+N$~Bur`qMAs6yLy*+-oTt)FMUSp7t z{+T=+;o;p%#8F5Y`@{JZpSIR!vz zAVnP#L;WOOP65Vb~Eck#Gu$D=;p%5LXRQP-VH#WsDdk(~C|TqmNh>eVrR zz7?k3GF(!bndqFQ#&RMd-G)iisT$27R4uQF#lqi}M&`=uRdb19we4SFKjyp~KaIJA z)4hBWwQdXR=VPS@fUCOozm?Z0eZ{M{&yzb4L^CUXYFjLz)f`3xI^pyZZOjMvvaz zk`T>o9SPP9*^F3o5sN&ZIOm_fec0ni2^R$^n2^f2^|hC9VIk;qjn8d=P%Di|rjN}T zIh4}}cE2==dchNL) z?M!R^55Jk~<=Ykz-oFjRray1-R(Qud6h-(=L4D8GQKJZTmF?<(HqzFd2246wcGfSX z&2Hreai@3M-RqJ|_id3xtR>a7%_dFD0D%$Va?0B$xyR{W$Pp0$IX`NuX}!^gttBEL zL;(g}xPTR}|+3y~s zCU1i5c!+pr3DvSVh=_pRuAKrfY-~grwf!W^` z;1B>=z-p_oE2!?UD)?0k%v(>or2-tu^xBJ^*3G@+Tg5~lt(dtjmQAwUdNf-sgtAt< z&H3g={VFGA=5AOHkF5X5y&*-+t{pG|p* z%>LnbR3HKbKEJBx?mU;y<>Bhm9+BdvS2qv=5E0dd%d(&Dcn_6(SzrLx9;xZoV(%Ri zh=_jlP~S+hAV9IxKq4F2$-6{&j1B8wEt&N6{bt*%>zmiJ*$Rqpxm)L9ziSGv>Aq?A z-`mxHg+ct#0U}AOnq8EsMIb~$Lx8oXPs~DF(|HrNaSS7MwymJg`L?abf~eZ<+3&e| z@Ag9fl#eam+Q9%(3--F6uKFf!6AkX8tjz$8>FzRL4D8mWgqmZ#s)%#XS=_ci6I=k92Z%yg~o~!xz2YQ`YHLmjD2P5WIwAqgC-NrXg%FP+~{|1V#^kM+PqI&v^Bz zahu85+o#p9QC@MiaU5kWx8kt%EP((rkK%0{D8u2vQbMsJ1YYjE^a3@vC<6IppZi|X zz#<{?=1{{frR^gm8*D#HG>o{^8L20X+NN$}`alFj*$MU4Jy#jz==L}2IGpOw;z;@6 z!bBmy2Db9HhISVxwWxNpbTzL_QF*?5f3CwayF}0EL*F`x#2@+!jJK8|ttKnymm{sX zZkl;hb5v&P4uVFRBGx?D$z}ioPII})%>T9sa}F>159XhmoCr+RMkyl>%r6tllplN0 zlRf;h&TINhA-RDc~dZc!0jp2~H zXolOv&F{kJwOsLg-(%Q)ldH{CS$m#KXxC2ep!nysiP$yWXL;P)Ymm(mPUO}-exR}% z$Po!GYmZ2V4w?$@=d1&tC;OD`AuSn>DG>J96{d|LMN#jbEvS1ST1NU=HoQ zR9TP!5LB%s0Rkh}>u#rH+Yh6iPD4uxxtDSb?N+U+wJT%@0bRM5K0}ThkwhZQR_yL+ zHSG~{TG{p0v!BgM@8c(jC42niwk5Iq86KS^J?2@jWUTUWX}=C-2nx%dNm6i{x1J2g zIhj(ouq70E^XU(tbB{Zt&U+h|kmRqY2zomyF?qJ3_3~WOdWl1=n_P0Z-2beB0dKJS zF;^^OCfifOUOf~Mo4}p7H6))r^^b#BS#{dcMM}(bUJV^?=n zzq;9??bdUdl#T>KWUyPK7p|O`GO`2#0t2z!c|&+!SLIxteI(+#g)3R*0D%!-V1LR@ zEKum*=5ML>n|{2dnoSl%B9^yX5aV^5pyqjw>@dV~DS3t4t_ z2lUHV(Kd~+t(wNoO$S?}*Q2<+&t#DPx^#=y%-OZNbJ|-9^d4h3SjP`Y~F%cMIx_MaY?rNAh2c<)`VYc$Rr)n!OhJ zMdPi<=~x=DwZm^xyJqKp`&C8W8P&8FntUHvp=--uRek2>}sO@yBv+o_= zXIX!P4HLw$R@ppjd}nJh4Z50uuZP^)d7%SomfaSgphQF`H1tIl3!M@wkCgh{+D!@) zvYYZYJWXy+{!)kJdI9BkcZ_O>g(g>_b&GQD-PKL08;l` z?a4jLBjL0^X#XS{LBU6(_0d1j8lk~E{m6d2(%^`QfE}HOKQgjjT840x*FGK61Vlg& zU~qD1fn)A~2n&DjC7JNyTEBQ*;g$wH{K7x{fTP?!q5Eat)=ofg51vko29@Rp#DM}U zHgo@zcyQjiT~GjsfFO56ki|*urEeB^7d|5EYo|lwLl%qviF>F7L;*G+^5yVd{i|UI zzh5r;gz$lIy=^_)UFfuT8k~+`)ALQX;eVK?FzYG){)bum|GMVhr9tXtMMTeNfIyP> zd*=KP-kh;7Ve5Eh&Z{|Gvz*kPFW#IEvGglSuWRdv{ZKlS)%^efAP9(xd<1Jw$|`xh zFCIOt00t&)VgK_b=0vGByB+bg4z&wxgbt;yqP|-U0+b%9=;zeSAd| z4drhqD@~5$hYW1fH4W>~J#r6H%+5TmOWqJJN>gX1)0OpxuvERHAKM(OEp{&B znR`0H6Zl`4VQ5Q0`Db~Jiur>syUmLc=e4WQJma^Ge?EnF!%vg>Vp~S>Ir2;*yYTU` zzp|e>q`t#PddKh6d~a1bsBGT?sqt9*kqap1xH{_osLN-Z9Blp#lu=&hSegd8?yA zpqK3Nr|wr4(1s%!?4AbmO7ftyZ_cK?*gn&e*4~m;C~u@?oPX>NEGUP6IQ0UB-s8ne0O}dTD7Mo zPoz$|*(SqV8nD`L>)c(T99`VvWg2a(rA0R_$nB`lTNPVa@M5KVhoUujcXzW{FxuAj zc8TFad3dDpA?{re5YR$;jCIwavhtIS4eD$C&huOH#sC0xL_|aZ7QqB^{R*INQQ9p0 zdM;xiUW{?lVSk^8u81qOY`Kl24by5`XzN?kHr$Mc?t$Nbt2MzIYn>jS$MLn^!N4eA z9jBPooX3-GAwLnK{BA9tFgIp0YA99E9lh839~ZFgM^xEW3)q}@t8CbeMnkF8KL&_7 zqODlo+tbhSrS~{?ZptiuZGyS_OW!?&Ibu*$&buqaLOm61WP~!FdP&yrdlRSMvw>gb zz`)Z|N-AmbWO|>BT*sp3~tuh5LEEy4#2BCoML9oOe4MX z4Z!pev{bOK$q0TH>(|-)6@rM2?e9aaRp6PMqF{+)HgzCP zzpi(#q0etv(yYq*i_Dwc`uJ1zo{fZb?*rk@c`;AUa3UfE0H8IDbwIzE>d^1mbqyPD zO5&w@ox$%F7!gv>uRT2RxrP$v?HZPj4`iCy0z%vO5(YsV4SbLcvXhnE5fWxntX${> zK=t;;K{*0+h!F@FhxB__M;TF5d7A-gE)W1fXr=W(;j9Est)5gyeQrP?Rl*QI;t+Ln z`QEIoi$2w_aao#ZmoF9L;=Cq!xexZsNV>(4I)9ei{bTOp@4$iUhT!lQjB3wx3}NK< zR%;Ji6vrR2VLomC<}Bp;OD?R^5f-04T+}O_4}oF*8w7}mfFdF&-K2ZxjMRR}!UY=& zYW4vN*-F>qgAJQc#fIm4wl|M*y1j9?uDPurU(VL;_J*(gO(TOPq~6^_R9%l`m~0hca!G-DQ$k3*mQ#9aJXmT1e0*Ghb^yWIlqUd(cxX^ zo$8<&r@@(sN7Q2!RcMx3l{wdo+i3Uu$J9ifL8krdk~g=r_{wnUp@eVKpET5$Ej)_Q z6x==3tr))PFf>BVc*p#1;_kV+GjCm~1Wyr>zQsH=D4k|Bmu<2-K}!P%Yr&9OJ;C_C zID#S~06HYkq9oEA&{T5bd)y`%C2gjAp(>n<*Rzo`mn8_S^4uh_^k4M(5FfF}PszAI z_ib1J1O)pxD=W#QW8piECaO2_7r-C3y)fyV zD&%~qsk&27I@_F-kl3kHKJx4&)aN>`ei5a(t%J(c`4HNAt_yAiff=ziIsWhY+QYmE z*5x)@#@0n{_hkx-zI#33V3A0%%8FY`ouB!%t)BDC=#-hC=U(PucS~wq5zcc?Gy9&R z5c#t5-7ieh0$+~0&TsN#cpXsh`Y+Rln~6T+WYezW)s=UilEsPKdyQW|JebG-$EYxv z^DRH!v!KeN>>npwY+IqY@(5`d9^aLf?oc!^V{35R%uuFXUeo-WBkVgJtCHZRZ0oV6 z8;@C07?igzTFp|*#_t%Y-WG8+48X>YE*E>$T%O7w<14NRBJgZBa+FHaVw zas+) zSc?#SwP+Ed3$l8b)w`_Td(@C<5hX+yoro@45JZyK|9Ri%+k1bRx#pbf%v^Kk(_Hhr z=h)-w?LOUgd{G5>H8SYea!k%F=aRBuxdNh!D&QRijHUm9sQo z$c^05G2Bi3t#FXPn3Qhi;^?qU3q*qq7}WP{owHvf4IWMV89X|dl+PBK%#SInNFkq+ zl(?f`ioK%)PaV)taZ+G~hN*#+cA|>?5#yZr04x|@Z`9n{muJK+N&;}b};G4LWa-eQUIK~WkpF)>lFgoFfB>D%(9g5V{)NpT?+=7>N_jiu2#@PEUc}cE6wELeW1e;b$WH_-Y zFFDe^%RV*JBp-jgrZ#?o)s!1>>G0HV_`J|M)WxdBI9P2*2PF`xF!OZRWF?<{1gBvx zc*D&Acy)i8IXYGGR`%9xG{c9F&P~VzKv!Cb%VrHLO2x_BHEZ z9$i%*!v}BFH!tiQ#0Nnnrs1Kr^93UK-}I_g>^inev=Gjs*BK81zQ;C{)P$#WN3mg) z6swig>`Z}E9VQf_^ki=Yz<2q-6j~Ego{0}~<3Hu6bcYdA5d-NWU{q8DjzM*i4b?PU z%q!?|lWOA7SVJZzLWFQ<3BJLE;z5ZL#u`F*ogVt>D+Pj(0gbP(2hx+H{(DD@4`pIP z0Vqr(iNL0+C{dn5_{TZ>6+-tt#qXWt5i{l^*>&7=2r+J*K;i-kY%zEHTI`0sh}>uT zd9|=_wu|nm?J`JMZ5D2s^-Mfv8q>as@ZO~o$KV{a_U-HqDf>eS&dMUQ6cEW$)vXe8<@yZ9b6mgr;DX*Q=#N<23xbP7V-T^)v~s36;!bOOD)Nr0^^=`CVOw|2?M zD8}$ML*qMTk8gMM7sFa4t|k!~99B?A&&9EibssH-sl5MnsTr;kfXcoXPTwC5p8W6o2rcczSgV&R4=9SV|2Y9;y&xDC{fA-9-(Cv*J0_`SJrXAR9}-8e zxs_TD=*59zf8Ft;A-z`X;L#QKubQrwEB|pALgqB(D&5^_Z%*8*$6cE`DChS4Ze;o= z{MU=er3K>ugH*8k)!C-fZEkMNGk4SIKTOOkiIvC&HAs@qRMa#v+@0f}sF>V+m^;k5 zkq|G$^vJH_vz~`$A~S@xMd>!jt56iq=gpt4xJ0DDz|&tXyqo+g;lSQe6w>pzGnc5 zmefNMrg<>GW-LR$xrOj=c?C%g)s-ApY!Olel(y_*MdG`P@gG0d&xR%z2;RfxC~I-y z1Ui@)B+td|9h|uJBvHW5Wko1KAp`N`;S6c4I}xV%U%O>A;h8k7j5R`4q!(TKJb&29 zdmW@PbjG5i_~2n_5%z(6d@mm?umshHrAHlGM@L8BPCFwiJQ*R&@mI@va<;jo#zot7 zHodt<(8ZKEgvxa0zaMi;3Sws!Q>(HqZg*@htnqS^OMAwkTG% zNWb3@>WG5{uLk*I;y<5F8ltqb7{X&53G9Fv57dUw`#(P5*#)YTE8{H+=@l3-7%0A> zgspxw@Rln&-UwyM1(})x6Fgvo#oNS@@O0Znu{Oy4GOALYUbb(JZF6f-@=0J`xb~{; zm}^oj&vD`LNMQ(?!0{0}CJdgG{))O!*(*XB*|;w=H**+A=!w-aho*8t=#rFGAY3cu zA~6YEtVjr!2hpmtURvI^U2MV<4m~3CbHA$*z|rXPqO}WcS0Qv=JU~srdKt9p@S{?& zjG4Kz1g_AeQ%!jtgG|)s9eK(+YGpjjIJ5Omp>Ck=QlAUBjrK6Me)GYNnE#3>Wqnl2 zzZt--{CC;JFd^-=VH4pSOZ#jlqz^C2>#tMRtJ3Yw6iF#oYF#d$vd3SMBS)P!Xw3!V{!|K(V24+fF3s0J#1}wBVqf<-Zy{$~V0@};Y z2IW6j>EA@tXiDG&0x1=#+Nc*CT0dnY!Xv_u3hlyKj}kt& zTU<@4OZD{R@LOOFwYJtnZaz+6``M(Y&86X^$%}gu3V?K;rkbCy_{c|<_zmvZhxw)D zdi8d(?u~gqy%@W*WaN3~b20h8@z`Tvwz)$=Bs>ZviPz+0V-~_#hoe>vPf?Ue!uF1z z_tLbTb|HqdBBy9+V!NrGB#I%jbh`q1_5oVeTKVI#)HFB8&A72Tswg|!Ey)P?dW_ha zOr_j96h3N$-2^$V%7k-(18&TYX*fD5d_8 zF{q8SD+tx{62}<)3BocULUCU%lXzAFZzK?3K9hv}ea8z#%4-fj0`|P&QXq7nJ%{Oe z()RQ7BZfVWcm)=_`thDwxbH(LYyF38k33)pyU)d~5|+-uR|+d)!hFwpNq=U-rucq@ zV9jMB`=9!%8q%4B)%0%te7C6>!DKa*oro~103 zhKoEF(-Qi_%@d+Nba4|QNd7uKpY-<3#sJt7WjN{*er|W4$94?;29)&_Iu(5F(YppI4Dlqc*$}+vh~BybTk>k^ii~uJqQKP# zrR7{tdU|3#Nf11dLWa&<9=YRM{%0zpfVB+gVEk`#iR@+^E0JL z+%>;%1J6A060r%--nH(9vgnc1H^8{0F8btpj$BykKer3d;eW*D*CGNpY=p!mGo>Bq zpO~bsY_=dW;7L#|>B;e-f1gHSCJ&a32ow;`Y+J^}>JO#-eRhGeT@m8!RIaQhT0YK_;U1&**hqpEaZD{;mY zRP48z(IrZa)hB}0M)4AxTOphPE;EhI_g8(?-b**K$Zb~Lb&CSNv4COI-tmE7j#_N% z#i7SI1Fs9S6s6PRV*7hbf1n^+orF{?c5#}MM5PY1$V@_%9rm*ebuuFbZCblnBV9m& z{?|DH1FjVq-}`NPXoJuw3i)ECWhKsgu*ON@nX*%0Q7AtzHG{=RDi6MH&B)UNIre{g z?|rYgs^GP3<~}4{7f!ma^q*TU{kfQ@C!|$3#Ar#s(rw`w6!Oi7LnB^L-rq$l;GSGK ztl*kA23h+uxQ66qU^G~NgQwYx50a|WZZ**|@_FY25Ahp6lL7g~BoVSAFRTo4xLGV5 z?T|3$Ta0)Dmaq&pwe!i#$AF}1>UC+1F>62N_KFl#cmdG4rYj2;jes%4{no$5qKfEh z!Vho8w$)1)*y7(b%2ReTuKhcObdL7OUFAk<%j1$Z zGqL6QMA3xJF+qV{_>^TAKoy9?jg*5*6xbHdwc`5tqZGvZBLO9?@G=a8iRTXw| zv~jJDCpe559PILSvpA2#&}@=%LvAU;?k{^>9g6yqqtZL65!-!ypv;9WbsvOCb&M$L za0nTaG;t7NMr0=$>BD#2`%}>iJVaJHQuQjIX-B<08PhJE$D!0zn;T$@2HqZn>s_`m zJ_A?J+nLTY@>r4gzTonCLE|-5LLMQy^J(bUD)HTrbH)2@IjwsMqs`kpH#a59?zQvo zv@gH-`6hH$M9KS|+akWmYlRoN$6fu3W6g%*Jc8T%uwI{ct;-*y_BJT{^gEoSK{!u1T%7F7%p{N(N7Y4j z=CUJ$)!181&2e`eZ5fLi4q=RY7b_Mb2#I4hN~HBwgO}qQ@+D95Nj!SYx*xyij%PD> zc0a|o8vEC`((TPM#3V9gGIsP0XD)?>N#36l3hz8znH(M`3HRc;$Qzltr0J?Q7D*kc>_u=6g@cF0?Xq7k4mr&I zuY4~qq~tlxAzo^Kq@~QlLtjdcu|9IbjMg-!WP;J?c>{M}vK6W%oHSH(a-5DS4%QsP zfG7X)2Wr(wWbu#wOlGt6Md9tGtj6xfyteD5SK@$i2s$*aH-Qn4R&jmY` z0I_+gePeS*JZe6{{_jxHL#tW%8$~BRgqAC%Vx70Bm|^1*FvBT)4Ybxt)yKLbIu;J- z2G6uxdoeWvwE1A`Ci0pg41M~OISHy}*W7ipmIM`JL@x~jiTqvOsE4%)Bi@2iGi2Yi z+ez+3V3f-!3GH;$Gw|)I!?4K%!eKVu-+7XwpBzRuex|>EpRt!-Yf)UuJ+7+C$Y7L= zASC<5Xk>&VSK_56#&Lgu5Yo{b89f1%QfWef!-Zd6+f4*MvtdkP)C35mSui4k@K0-Xo}{;$5g9RKl*ftKY! zpDJ!-V9YVLA1+S08C@@SvfV&MS1M@;SRiR^QO5RS*~hJ0uf~^*r2W%XeKUN-_8jIV z^X?*Zj4d()X=nJ><5*j5lD-u$H(_g?*WR7oEGJy=h=Qnv0qIdjaL>EHXI;SY+Gi`x8&AZMkiV0Vp{nhRV;Hc z;#i%ej?wcI>k|PY-DZ%6qtRv|vDoYynK{SgaCLG+Uw10%fugh+Y5bt*x?d+67V6_n zj+52@ZJ)P?@L-%`8M2x=o|^i#EkJW#3=!2Ra&bNU7p=(AXoSAgL!0!-tzPgXM2wC}Hr1 zWJhiufAee#PEhJq&rn?2{-VG14`z4m*Wc{(M^aa31ZCv9gp-1M9b}i}>3<@RZP`AW zTOqX`SV~VFb(KC;x%=TSbnGfw-U85XeP?~T*ez0tNw{Sq5fe-v0M;8npJQu{Y2r>6M%zm|q+ z)zCAI-QU^Mk80H}d!YHs*u0f5BeFG_eCpEW6q&RADz~er-H>>ngsg>(b57Pbjbd_8 z^EL8Nf^_1O-9_R%H;IK(WJ9n$qk95Ja=0d!Qxf-W$fOdFz#4`pYtnQOCu{J0G*Tw! zirkjm2z_^*x;L-tSqYMwcs>T zyGps6PVdxAhsf1-`Q2fx={BcEGkPyh$ozZb85$Ve2!uznSXWkY0V&|AA&_L^a$O`h zBv~(Pc-QOjqCi^4D>?gni)&_iu1!e@I%cxzcX3(oxR3t_HdGE&XtqE)ux8`O)sW$H zba_7M@7*)bOIhl`j<|hanb>rN{7R#E21}ZNs7FIDmZl+6fnR!(3B+WKD3;FmK)Jh>`ZBsCs=a@OUKD2_u(!@N z`}Yn6LLEb!5#PebFYVp9LHS#ECX6w8d_Qs+q!Q+pU98^J=-WF{KAjO(ERe|#pNWmh z<%9hBld^sB{YZxN^^th5J$h9Z9Dd*vqZ^phVThchngU^fo!RygJ^4gu!D7n&+gpA zyG5(5?ToCZ^XxuLXoK}$tJ6$)Bd2L0vSa1Gre@>JqvqtB#G;O_&Q9MfzJD;^E}|Y zJJQBu=N8|umYBmQwW->|DPJE}wYB!s6T~R2nn#)HgSN99k4l9>i3J|3{Pk>&D(=2W zA;*v2nL^CBU$vO^=4xiWot5fK^{SKX6qx6f*3ZtYGdpyQ?4NwQ)|(w2&SZ4o}zq zB&IW(sdHh?%WT7(e0VMPN&rsvHDn_c}M>|EW$!=L3lc;|{otm}UU=g)+^kM2jSNe)-7EqN)N z`cBYnvaasa`d1cgm{vQC3zn5KKe-JE3@jGJd|I^3*t_B~o?Dl<#kniu<)duH>%007X^;?RDkrA?#FV2|cT z%`kzJgus+Y;@((V!hsQkXn{0Z9OU4hAQ@N=ZuT@!F&GK}u*NWiIKY3ciiiLV zJMbC4hZewrHwOJTB7ps!`@ftR4gwpb0HOUH6_AIh2qyytKL1C6TqwWG-EWm=m0v=e zLCif25dYUv5gpq9oT6MLb~&&ZZA z935LFS!Lvu%vm!0C@{^9C$e3EPtZ$3TP6}o94cffm%-YuLR9c zi$KEV!t)>i4y<|qOk6)4&0vIQe7*=A#FF+uQKl8i0}ugXtS}-jDh^^}T3BObXc}T7 z{B-|F0Hj8B2c1_4~NCJj?(@|Gn*6GMnk*XC`@6;UBmDlHfBx zhaegm=0oh+!q@i3=IBR>T<5+l@`8>t`r9lUexzjj7-Um*N9j?IhPUF#{85{+nok-^ zCb97x5;Jd6;nxhe_sdtPW^=L6OgM<(SZgzX{;NCBV(7J`R3px_>hRveVn`B6AFd1@ z=Gyx`=_WVv+mE?99dL7IkAcRr_OW0WQ{C~M;JU#T%lY5j>SduwQX|5&n!)EZ0nEKd z0w;4K&P$Zy?9IE4>c9TXbn6xOQQPf;mqod2VprX4Z(mY}`fkgSVW-5pA3e{XC6bqd z>q2ka+rGP)(DX4<@~hlOcl&wv8HEnmjDmh$<_<`(8)8cIB14b^ad04s*lBW9-X^{WQip-4;1BSSiI=_TZbH$PH> z6R@ZzRqpGtN1$bKABTHf9$Y}SW~L;f=ab=Ph@o{Ds^fJD%6;j$=~!4sE@I^#VI0)4 zR%yl0mTXv{A`agM*==sN6f685k=MOTROWf_aA2 z(AHgBCNj@p(JCi=_Fp}QmOElV41?R10{Xnfm6qH7@-pi%;)!EJ5tF{nG*^xA()j-D zjYODq-B1WN+^lxC0U8hM^4T`95MVj`KS#8$f`HR zJ;uzP`3Wu(Ng8T)5`{lJe9ZH&3OZYU3j%uje@a9oz1{$eRD{}Sj&%cuq-A-LV31W_ zsSz_IjtPw3^juag?J8BG8IA0LK(>S+%@i1+6Ci<0r5tB&9tk!*v#yM+>DAFc@{DEe zy3$^Kt<*Qt`YMfBoqC2KAIpDYuhh6|U$=N$E$(Q?Lmt|gFUO^8o8%;t6Z5m0=Ia}7 zaGV?KG~<4l!6z87jCM$qy4Wu9mrj_Hy{@EbBW5S0RVijtX{23Hlc<$~3_S9jils%^ zw2dwF`61k#?)`jH{8eqpdF9_Q*Qtpb+d^NT=;WV{itc5FVdL!xbT51{TQT`oD{C+O z3D5{7%ZPbgrf8_>{jU(o@5E-%rztaRZf`NY#Bq1baS_l3vP}Somec`1bdegTr@2-Y&tlm+tA-OE)6n2Me<5#+{a1 zKI=c&DopBfm5O)S_HS*@7DUg6Di3t`(kaGSpWk@acDCPLp`6y&OFJ>Q*Y!Rv>*BAn zZ^PZP-+wK2IT1q6ztH3%gp+0w2_YZh%{cx%bq`J63V&Dl!6a~Kz47-Gdp-q|p07;) zg-atw)W7!;r}s>xWSuXdYt~zoi3k&xSk*&bND*zk|*nB*;HMlPw(wgxQJ?9SVZtlDEw09gQrBgnbP%{+is^v9hommX8 z#Th3)zMmNVjhtML^{<@ngMe@>^)o(_rE>7LPYlY6v;W83{Kz5il^U9ObK9xu3PF+7 z?yy7wgWB_ya2i<~L$2thKJy%}#yJA9cBA}f$t#h4zo1>A*C{LqbkP=3;TA{lu2*-q zh{}!2-#bZayE+A^q;bJ9+R8q&Z$sA~brQ=NLHwe#1=kj>_)^>@y5YOljp}5m8v#mX z1)=}K>bOAg;#i~oCp}8mu@>|+H|m5&Vbpnq{JeBVXjjEBd0xiUW4I=0f?#REEmbNs z74#g$VH5HV1R@$<6wS9lnc)hHBXTbkG-JstkWopukxKQtCb0-3vTXZjaS5(7X#ABl zeDog(eh;{pLSG#gmQNY4TEiIF_*+(ndIK@0#|NPq2a#rptKoxsdBx?b3lM`Fk+#n;D6ICIFtB@a1F@qhov1lL#QO{ z4tpi3Rl2^YdSAz;6K#JMo>M~q-taQ9?UnGzCqM9{F2tNnS|p6&{$u@>+f71QKbkCu1OU~`gUp;iQoqdy=GzP3!#Mjrygjz zn{Cz5wNq@BG{&S=O4^`TqyDOir=?X@Zn_!?l6=jZ_ErH)e(DF|h}Vr2W(dTT22M(o z{)=FgW4jDakeLXST?XOOShAb{B_vad-7e3`Bj*3Ok^WGtpF|PR!PSudfL{aN>`=4d*=}c7Dp#j` zky5OKfhmRTx(HFXp1S?%l_p#KA923tj?sNS_2Us3>p=p60r}(qOCY92^`F*pQ6djh0H^f6$UxwpqrFdN^tF!F!Z zxBEm6u?`55BA8j9<#xAn7%A~)4_oy+kQ|S_Qrzj!>gmhDo#bBcr!>L&AMX4&u#P(! zf|AW5PUgdP-K?t|X5f&6rZ-1Vv9NIM&a7CLumjZ7BGhpuvJ~;GC8C7Yy3m4gHKu!< zFC^k-#8DtHYhZd+l&=L9aw7^zVEn)8?3AwfHWCbS3F7>gk+JZEA*SpqsD$ts>d`97 z3P9=;9TL3_H`KT+n}wu@hbU+vH#(MBNu0LsQmllzwsQyhL zu;9wn#dG^aa9&A{yijM%eT7TL-fs0b%LYv^zc~gL>-N|$LoNhO8z!Ri{vGZ$B?T9J z$tWE3@6s_FR@s!K1)utb71ZbGQm~#stT0<=Fo`Da8U^_Z|lcVabYdx z;H?=qZg*p~oH*c#m{21BCXgQPu1Ptun=*T-@mU#c(hD(-bXCqRphs875{|Vd&}J=dOG0mpXoU-d%!u4 zjZ>{OnxxZl9)29N=VDMIl?<;`bBUT~-b>b*5DK$50r%3NlPLD0n)iiKHcEzmmb6qq zZ8hg!l{7Qwz+;7%G}lYj>5a@FrE);Gzt~_jBP?3hK+z4g=(8-sVvgG8)N3uG`loa} zKg+tHKdU-!v72JW`svayLY$gz99I8va+}Iio>ZT-ZQJxL`x^!;V@b}3szC#s$Z-p6 z`v&dI-xD}JP9>1cO?<|o7KQ>v;gtSfO6xi9`k=9u|?4N(9an9gG3oRD!S=uPyYNtk}tBW*-{HT`DGarYqw?H=G z!td4JA=H~cjXB{^6*;Z340b8mWe!t5OwJ73`R#N(^FkmUNkP zk%l%qJWaru@rrGLVFiu;LidcW|1h*U#x*Q(BPs$%i|P|6vQ&IdKs^oTtb{=VUNBQ1 zpDGSCfyY#Cg2q%T?68l`UHL;YcAomJwDey*y?g-wM7PPs_jxZKi)jHy-X|WB1{u3A zZw^6g5fCSCkeD&}K~SzMtuQ9ggj*1cf$=OFesl4@ktZ|Hov2e0xE2_PX-Y3=+gIV% zM5u?G7s$G)A-x7sqfK}=xaZRA(VUKgcMO&kVGPXWUbdayFAhXaxWCtcVGG0%)%I{C z_^JNEhA|Hh_F>|2yV;m-76QAEEw z$xi^~5QG|(TTGINjgPDRRT+N6%8k*&K7`UvLGWMEho>MXad94an6HkVD4J!s2q2V% zGO9(0@+;;FZL#rq9)f8=a7AB*jdRLX&dV1EwLDi&eZGjm)uX_dWK|S&3>byH;W*0t zA5TK90I8iZ%AdLlmZzlVd?*iEPVeQo`UehbbWo_jhyeuHJT6>mm~WGS!EIq5WLxYP z?(rp}GR1}^i|VM!r4c^P+$GI(Wm|^M9X|u!!X{|mqOy^|>t~@P z6-Pb|-{_CuEV9O;Vy_&bVea|W9+wO%g|A?Z*BDG43dB?OyTtsYwYt!X&8q2Qu6PV? zY}XRzl#8CwNT3CdfdFDTg=p`fp`|BSw>b$&3|%WApCY#vm#J}U;9N|DTf7U=h{0me z4eU>#*lx01q%SK76hnZ;GDe^e4pE_IE-1<)j-ghqXn2{Sr>EnhA5N$j)edKDWK_m9 z&i8{SED(o7phZqO0GRz;F~iJ?5`Ga z9STDQLxl~E6JI?W{Z=(iLj?&Fmkgq-{57MjClSx~jQ7Df?(wjZa*PpH@@prEn`wY) zYdDbrb}cS1^cq_hKmKPp{IZnVR}FjsMZZj#zYHGUhR6B@?=)CC6Dn^W?>3Ony4;vx zO&^Xe`$o|L{D4rSzU00c%Sm5z#D*RHAz#`v&xw#D(Urx5QJ4THYS0h~P*E3v7@n*6 z;&J8Z_2JPypk?LeYb82|HrDZrrp|cE)IlL2`EEsz!G$Wu7|lUd!(jMP03Riv@Y{rS z?A(I_FUuK57=psYb3qf47Jb~2nOp^7m}*}Ir8xFR-XkLbu}oBu#`#;mbD9LH;y1z3(6E|! z;8w@#8k`xN4dugAZ8_XKh92@EpBp)2Uh{y{5mNknkii&;)cy#9f?QJmMLy41L0aeBiy= z_^ZzFX|3;AXp8YdA7`zkfeE>_&Q_n3T^32`NeeWwYa{#sJrnoGH+>3jW5E1T5 z-{fR4zB;`XuQ`KQmz!`_-Z!T}n+`L~C~Ie0K=JiYqOISQ`-C*Cv9PWNfF-*e z;L~2p*QMJm_-NKXuyCR6Mk%A_GAATXI5SdEJ6!{8^ZUw7$ z!`|`e=&B+pK>7A&E4cTPDA(+_Rfo@J3Q-6QaJ^5j1$XF!qH zFH};tffSF}9Ey(4bo&}o9I!vi!uXq1Y__YM7_0Tx`W~F+<`8LkpiIh~q^&Ob=^tPq zSG#x!;<-yfk&(uA1k3Ql4!$!!3=mqHN`^Cnv0C^+PWs9({cDR+u zQ5uX(%Fdh>dS(N+M7kA9#kWyouoS=0n$dOhCBnW$ZYL(drx8K$qtVH=X&ZXE*!fxGcRh*73ZZ?0aQ5jfH{S4MR}K48#p$rQumoh9ZRCca4@Q{p>`PXe~Q*gf8o{i1;*vf%8^wo&?0 z1Ct_oWqXwpN}^ZMg_j7ae4gC3R5mWs2VAsoxz9cv@T@9dN<;63Sxa@d?AmR+8QPb6 zWX%E*&OGascU!97I!52bmA*#E{Tb3_{>RPv9NL*Wvr190{HIi8^Q~H`>jfU5!Wfi} zKlWfrI`AnyP$X03nhbYW;ZrFW3lo#N$;*K|lq$;Oxl$tdM!} zEEdTea%K18=_D2)N3+^ihKp4pGp7d9Az;foCh!-9E>kf((8S{+yVvv`ubD;J zlI&tHRrJDW-rynL)((!Cdir)6O!|ku3P*yA$eG=vzyNQYw8W zJB$#!BU2yH+&jY22Qx$T@pN+PgDS>9yZ!-`hyLk>djkl9Py!H@pR*iB$<9Ump+B}( z!5gc8n`kb*&Svkc(B0eWs<(r~>0v=6bNvh~n5^5V9dALeV)ALa6M%&EJj%xNEfh|% z#_e@XSng$B_$rv2$RcH2P5cucGuPSM31kO|nzYn+r9jzJ7N{7AR%VVUF;yOJZXO=} z_0Nt>NrAOqmeg?otOdLjD1^!P8^XbW8%IigYvnQNA{z?-HKVoSM=v(QiHP4wmc95U z0tal6|M(4MdaxdaX!Y{N?Xk?L^3+&v=BdWJm-TJry5Lmb zYF5d?(frxsvxmRU$_6U_^6VPzhp>X>TTk$z>{31@>b;Hjzt>NIT8!SHwie%-%rdEY|!BT7`uyH;#pc;5Vg7!hUZR3IQz{-I1GM--0 z`Y#@KWKA**=kR9(FOU?|SYxjJ0gPlTB>|%0^!nwXAg%SOtpx=1UU0)AMz-5( zRudDWj9q`F>*tn$t;OAJmA!5$3*s`1sFHkAMHO2sD6%Cvm6#3Nw-%;wKp3=IptPO( z3`@!M2nG;KTLbk23ak+Eyo4rbVxmeXc zG2Ser9cxgdrcdK3(rv*3FwW6`(lmT`6b~x0{OKG8g=boRqtI`5&Onc3(%@G@?DVTP>{w#^i`#9%h8KvChL_Sq;l*3?7MRa3_I5b|zu>V}O4{ono5{>~Vq| zO1|1~XbXL+ToNfyL! znQxPmO8Y&EwU^-2F57i?E^%MpA66W!Z(W6n9m;-A{e??g!KTbnX)#Uw1Bsk9S;Um@ z%BQ&H+@=umLI%`JNDcG*2RbQ#q0RS@*L+U3Pyk^)oOj4=b{7f?^P60{l{>DJ|lwFXjuP}Yei_#ebefR_ILPGRGpyf(Oju7ncQJ8Eu-xTh$G)X zkX_$f51qAZG(^Nlkm1htMQ3AcgqE~-{x<;10N{zk!eC%waA_SQnb5V6_eqQ-11c5Y zon35O7mab7U8^2nn~WATH=`J&)daBceg@bGE%RD{;DKcD7gi^|o-u)j1r=JxrBfUk38LL%1sAnY#1Sc)y`~QLftH(flASmS zm$tLjM?Jnz3iWJobY9V^ZYTBaeJ{j8%GPL*|J1CM{eRm2zha}EWpDp?-~H;f9tD0G zlfAx9wiy5Sh8>3ve*g;$yFUUf@kmZx3MLf1GL}VTSpFhMMLNWY&=N7ZuiD6W=V~A| zaq{iE*UhQt-j%KK1Ci77e{a$g?3wAt_Z7*3*a}E+-5U4e_ZaRQmr`$^2m|@I1YNJ0J)?(6z*ZoB}rFyCf;s_o=YLD{O_YWTW-`tC8uYdB84|Uq} z7plw8Sr6faIwsA`O`_X#ah@b~62v`E`SvOAooZ%d1HecdQAQH`i?~lQhS2DS(Zss%q3kTO>u*ZJN)cztx2S(fNh^TB(F8BKrF$!mBB$zZ2X*Jfg0T@H$B z21lO4S^Rf%`UwPJGak9#)z7Yt5+nClb9$%B4yF3vD2gc^8!t$~U;1X(_>$a?|U;3uTv6_8a2m=YCZgLe4#2R0ch*^y$Ukf|HO+DL(dHL^qgh)bhkB>k&0n4?q%|Lp!u7lO zGs`deW#x8~C3bhSgleTeo|l*W z`u0Lez}{>B=NqsfFv0~mgz7~m5YMCFi*144z+W&m6p~?t;E{!uHrrUgbf;4?Qi|GV zSOz7d1m@lLhM@#~NKp%3c@~{BeI-L9I$s)kCLK0(&qY|wg}e0?HVO@fglRQAVt5St zMrig!1gCQd4#Z%7?pk*MSa8$qmCc+jADaYAHpu!RlJvXBP*PtTJVATGx>1!d@Abv= zQIR1X7uxKF;hps?1x)`1t&yg|mSKt-ve)Mg#7%0H*2V$a?M+W-*KT{{<8w5&OkoG( zfa0G2lmIt>{a}{z1JVNuj3mMYD;8zg1SHSA8x^Z?)G!0Mr-NZYk1>39`Bpyb8H%NxsYq3ogq(B!K0EVD7~EBf3<(MdSpaV#!{e>Fm5?N z_jKY&?Emwi#5$2%n%@;IA%yYmpVe#OB}JpLj#I)Pzq5D0eP=3o^vmSt@-em%Se#r| zJ3Kjfrjo*Z!%?+wBUL-ZrbkX3q73Wi+<~V=u8Feezd<%{)AatR>I^1oNdZ6#16;?K zY8~*B@jWuF@{Psy?s&$F{q41-H~QA-2XX&JA90aZ3?zdSLw+Q7KyeHfuu-b-ll_~& za6JJkN`$>ZT*7!ce)IFEE?UZBeKb)@Ga)4DA+W-OIn#iX19kD2cjRT)mm%~HQSuqF z`3BojzLw$Vzi=BOiWPl1c(8dTQ0OKT{HQ(2N9b*)hi#i;HNm^=CVURa?RZe_-$N`T1O2*| zi*Wr%XuMhCFB$O5aBIW9&j-XAc+Y{BlaXz#2Qk)~{zq5`eT zd2Cz@Pdzx9gjB)vNAffYaXeH$8*N7-G?*ZcD??=RV^A=-9-Zb3HR~q zBxgy$;-H&>+?n@?X3+L=&O%NrlRCMnu+SG5$y*@@lP~g{6Q-AK5;$!rRR9hMA(H35 zl?vXl@;zXkEmgh?a-&W}sN{s0Gk@M)4GqIMI9KEGyr*;wgb)n2+^)*~c!PP5l?!m? z^6h%EF~Rux%epU6lQ79C9OInz^$Qe6&wGfX@QjFtr*r^5lF<0<_uAwQ=^rZr7Vp8e zH#FLbMb|FP&Z6@+-P@(5eYX(XC%1~(DzMez#N5f&cWi~vdJ(56n$I4Y|2{?%jhqO$ zUIADr{H&(I`tj#r02CfAu!{BEY_g>KmX}tS5NahKuRD3r-vx^9K;!j5YxhHGMka;* zkEAS>Y3hKBxX*0t2y*xd_-_||q(*9+kfd*{>zl;*t6xOqTNeF$Melipx>G|ku<-Y zHX-E23nl!V2McTwFKATlbMf|in9XaTVpo>31go_HxIeP6(4Qu?akfnMgVi4D^hH#q zAh1XsOn;b4^B^^FSqFuhYu$MqeT|d-J(T7*`3YtAT@uNs$>haYb0G$8rC{(n8m>aOid) zuX-ENP_w$J)G@v!h@RWE4|}AfAx75&SEPYJI~XQE+hZvYuDfGXz!qwyc*EQ|Gx*dO zPHhFg7O;)gv%IK!BPFP;w)WZ$)@|K)NUpN#UZ(Bnf!T@ld>;}>=d7-a5i+mULhC@* z;=mdb-0oOu6m}aF(Fq$7QuCYu!njka^p7f{nT}LwYiF-zuL~E_;Q@k7FA{X!>=8_q zTQri>ZwzAi-TQ^&$0p(_ZFk({LyVsBtzTcIr8U;#F~zuK%$IC;u0IgNJ=VrwbRA%V zEk?4V>H+`|tPEH+-0Cl{){M2GE1u7+)azolRw-JPloTY0+lM(*ZIHFeaG#|~nTnD$ z_-ht0QETC}IgD?1oj72FS6rRfD4olt?Q8GyDHjz#6bmtnMeFO@VRX=a57Wn)Ll`VT z;|AM@@D?2=gb%)}CQ(Ju+4W4Hx^uFk#prut=S1_jN8)2-|DT7}^2e<$g*O>K%C|AP zkK!V!ZeD2IV)zC#_XX#dQ1`!=xz<}{68{3rv*a^%bnH+454L&}avOz%-~sRl*Zpn| zBMq{fh#9JvrY*Wae`=VeGT{+2p0Ab@KT@%!r5=25@F17nE!-GJp>#4`KNL-nsdBXT zCV!Xgh);63zw|@GTF>u@#+HuW7f6S+5SpEdeph7cE$sMbL;3vl`mR`By1%hytiSF3 zE_n0MI@1c!!D2ariD<8o2gzrAds*QJ0^C?~rVh!jE*3uEY5JC8JzwF zGm@e{BL)O{D+-|7O!IZJ=CebyN5atgDu^TSGk@3qvgUP3#^n>47bYI-dH8!Wf5<2* z0$aoE+o@?M6W$Z+X-Vza{G>1?>AZi%I4^girJcn{Ce5NjmM zZJ$o()sqd+HzAUTqPPV4EkBznuBw-VDyNSi33y77cy<0jXs#rVP8)&R?{=&B*LXsB zDb?{+c1UhHs`tc+$Ly)MnGh|22LJCpHoGis8#DMnAMOv&mjM+^{ep%LK73A(o`l?V_pet$NDr?5JnCg`R5q_RaQFa zFUj(-r1>4c&l&9g&JTFlpSMt0{LU#=-zeaNA4r{2OXen))%?Aw`h2%X+D)?g7TYp2 zYsl$~K^eY(r=Nndlm2qoTd?;Mqn9(gN0We=@ZVAX|4mAjsM|eM^L&c7ZKs32+r-=@ zlLS*EPH#V`0QE=uIJvlx15$u8doQ;B<kC9*#%a%p37FwUQq8?Y#8TiRD%`2nN<&ZnhGas6P93)!rJ~cWa<7nL?UO zxsm`2IDk?r0u)0ZhJKH6&B4PhMeSxaCANv~4dUYjk+7P?qv%3L^cpH7_#{Y3j|zbz=)Hy z919y=tV@I*P+9h`lubI#Qf%XKGEp*4w91%trbV;SH}M*Kqw#P80u)XWA{)C}cWV21 z;QXsXJmaq(9Ux#{b7adfEI?%~OPqRF`6~CkU3BeF%Zc-R>3YoX z7B@OS_YVuJW&Lb-cOt(JV*hMh*U}rUe9uP8x=OC_pF1VZwwLI<68)h(VZsvd%TF5s zQ*VDLJfQ-^=fO(`pCyKTR`o;odsZ6Y?HEEE^fMvgg)Mpg&u;CDM5{Gj`?ULxFFg`M zO}4%*9wf=xg^@2o5gq_W*C^=(O$`?R_ z{k7(2O_&Agf*3;ZzXGOI?dm1Vr_4pUt@Uy81^x!vII(ae?|-uwnlXgW?sOpZRu z+Onwn`;qC$j;d2CH)#%U58{hvF@;NCA_YBxPV}(UV;Ej zcqwLwz^vTRdOsQtt*79FKrR28=~2I5qB^H)eV5%;#gmMBB5<7T4u1B9L(w6Vx;S7= z1UXQS6>>^|msHr8UpP&enR^=aAD5rlhgD@LWpx377-=-+>Axk-P$nH8wvP!GkkRP@ zoc_^by1uk#F$+&!u~ihQ%$JZVM^4)lJ%Y&4F&+=AsdsI|M22OD_Lh4grC9NOJHwQQ zAMIn0o3V6{4_inzffe65lvO3*Az?q7VU{5AslR{BTKYYH^2*)6xsp776_{V`%- zGtH)8H2Z`z0vXhLc9R<5*i6#(7U$iMrvDB z-vf)=aa?2y7MI6p>*NQ);Joup8s!EYo*7#Xq{gI^NR#8`)2HMcP{r?~wr zV6U`Pz$>UASqTMMv|N>oWki)l3Y`DmoPBz%@=5h-K8k$s`!Mmglhvn&kW%xm`jux;2he_v2KPk0XE*v?mC_c@_={bqfjjyQC@}I1X6kL;Zfn|8AGofY*c+ z7Es$OAH;V|+4M>4<=Y(IFi5Q4Eyto2M@+IbdHo$Sr+;;&wcvW3_(xP9HvHwke+MJ{ zUpzz@OPh4>WYivt@5~PT-UzdK7$+#luVgjTw04v_Ev64HTt4XsiMH;#<=$9w)Yj=P z^N2XU@q0Z?t)7e(vSE=F7M6A)PfW&YgTev`E<0zK(Ka*Q-b8fi*{Wp%ztp+U0%5RQ zMebloSY&wl_}IxqJf|U#LV~z5@%`Q}jRCt(>WTuHgP9*HRO)emxs|3o5w`X%!85f8`F=Q^tadcy$d`=KoMiN?68-O2&u^#)$F+ z3-k1JTFajxGZ>mdb~8eD6D%x%bfyfa=r1Z5ppb6KAi*4uo?G~CNsHHcxS2|Fez>q` zMad+*?soX>`)#OqF)PI9=%}pZCRaZ+0Ldy+>vXRE!PCbg%& z{bGUka?C&DUAaDK*^tHXL|Kgl-A}Ad>gdcl%|Omfn~o}*KZ^;iGNRpD#W|4Jp)YYCS?{#J zIFH*%Pg$8Y^uWc~I=L++=(J9bR^wOISvNRqyJPUFF)pxp1b^<5C;Zf$rW|x8W$9R6=u5hVxGg{mZ@&Zb2jI0_X0j+4+~?#1W=Go!gmw+PQntHPPlqc zgp=s7E)TgnLcNiV2;1&vvHrW7L)W25aeE#V?n4#ffv@i$x!ivI7dp9*^2WfLmiX=b zX8_rK1r#RvWY*2(HGv@}E(Gq(cvLE*_^mr_uwrA96kGbImem4F@Il0qjxN~omdNnHa~7LgZ!UbzmbE}W^SRcxrzg@+P2p%4^61w&;P6_F| zXYwd^$vz4wPA|9FmP>6@!Rw;AWCzZZZ*;p{wYmGPVm(rr-eWUE zZloASw9$STNri1VukQu|IzA42ee(nrgC?fD;gv=_r! z_wuUA7!$hYcA`zu^WPoz8dFu)=)3C?-VtBb6isV?9Wuz!yz|8Tqk)7le$=R5uV4S_ zhlABWYUmx17%x0#nbPeg);$xgwzI+7?_ic^3#%2+Ea3foU_$PQ>qyqp&}76wTS(qe z@{nBlR8u%u^ED-!cX)9pKQva~`c{oB(SL$;y+1h%rI$Ci?3c6mFRm5uWa(4*rtz+c zRY|cM*>KCy&)HJ;2W4c@rwxV4Uk(gD{9F?{Z~o>}o~>Lt`EFo{fdzoj*$SS1ojaa! z|5;PIGhIUn|BAYhiw2V6!$a%Xn_i+;jRNe9J5cx++e3^)E(S9Kl-S-^R z#(WT}4&?nnZ;U|6-fam2AOu_=u8VGU|Dm?AoQW2MmH-uB8eILg{~2fOOYE#&`KNl^ zH@4>-%r4cBT8ZIfq*-}k)XakXiD`nq70K~7f`|2Kf-hd2<+O3816^}-wQ7MTMj9-N z-26jV9p9bVTHCh_QoF%{*dC&&QGSxQAU(EKg4tIcoyqfszg|VR@l&Zq!Y7*4AAW@$ z2L)8&IQ_R^M3un@n1H}WZJD$S7fEuk7%Gjrx!cd#%vpsg0Lk>Z0SiIOfBnB;Kd=YD zB()pqsx?SCk4+^1eMNM!&Xaz?|KljQ(S_5<`48Kv(LP;c-v?%)E&D##4h^nGHhsEs z)S&5A%;(u{q?dmfeZPCk z39ACRGqJAoriYxj!o61^{sYBMS#?p@*S4=ojPwPuj%<~90WTL&B6mN5x3URhc8YFZ zLT*cFe4GyLC;E}$<^KiYd2Rn!l+iTE$bo5sz*VmZ`5(Vvl$EiOQ79qBh`5yEfyyC>$Py|aVO8j7!zE~_#KU2d z2=gT{&1r~`QAHRN{{e5yC*VJI9E7-_qW^PA*>fWXDgVqHj8I0A5Rdy0XprH9!7B0+ z{{!(j8cJ9R%s7w)0&WsD2x6?PfIu}Es2Wd{gxl3hM|wR7Q#bfqS$&={#43gg7d|2c zL=%V!l`8@VfFV(Y(0LBOG0HpD_;MXYaA@r4E$5WRi6WL?pUw$t{}XM~VN}2=(f}&& z@)1GMECs_9KlLoB+W!M&K%2ia$b8(DwFYNUIWt^|CMK$SPgJdgx}%5`)F@qI6PxQt zSy9)nNv-vXV@aGK&a$ZBXFWh^)i_#x!&s%%;L%h=!Cq zvZmXJO*We7LLs&&O@z2gMw~E)GT3w(EsSuYARuuG@J}*Agj-OIwF99<53w>zQ3|9L z$p*Ie_d+hj?<-rqmY@43`b3*n%5v)DYQ-aHU1-q6n0c1WUkpjS}!G zhlzPEi`fh4RJ&BJseD*3YU?Y~%FR^kKD+VW`}rQHrvI&fT=@77IoI#qAMv&Qb5$w=F3YaSxshSlO zg$o5JiQxcHRSGFo8;qYLUb`%USsSp80V<+Efdn-x2!L*G4z5P;j%Q3hu*<{efz#(* z&;UXp5S{zCSHDfBm!G^!@@x2=Mb1Q=!y-1jLqEht?@MEv{c(B?NfLAWlf9XJ9!nyU zM(Jnfd!TyBKE-UdZ5r7s4NRlg{Svr9fEp%klyztXLw57eY{-~-SX+YWTcZgM#fMtJ z5ddL?0T2WcY`1`bFV);aOoLt>)2>yXe$vAj1U3lJ0K!4)+#g0!KVW9^5ZpfJW7e7G z0d;@?z%Wi1DA>*JnxIc|c>m9=J06SxB4r(*?6Tl$Der?{1Nw<-+TVxhN5PlqPyiLi zh{5+v*)5tux6BOfewD|J*Q=jGP36+K1C-~6!|AxO`0Jj&GZY)7gQn^t>V6j@rY!{qGv(`% z&X%0lEoNY&NQeMIqyPaI@9R{V>_Xu7SHjJezrT5nKyPvP5EjTzhe0BM0RZIR%Cci! zzjSM>m~h_uerUb4U;yt;c2D_l-~j4)00}PAG;GG8>{CQq^5xzv)_+9P?!LubsE+`F z5^2?ED&D(7bDLA8gQn1=Ox!~JwQ$XcE1glc*z&U6%&5fcE;f7l5$dbE={hv&J68E? zUV2nP`_y7=#|vvEVdDGetugoa$*+pr-m4M0$^s5>*1!-T8dxE%k!mRh7UrsjN7H1@ z85ag?H#p+VJzrWBxkY;hckk&C1X9n?e#EEdPejF++ZSP#tJ3|b_E0HWI`t$sWtkA9 zwv|UsQUCxJj%%XPmIwj}e6i3X%orG4lf?~$ZGZr1VVwKf?VY*;AWLF&z<4@!a-Y)_ z3J3xO12w&#yH=}r+AMwRg#Fs3yjuKCmITIkwV=mlJ2U0cv{Nk;QhU`UUY^0fJ@mZ* z%$rNND9sJ_?%~axCWRlbf?Y-S?9#fIFF##%Rk&}`cfMEYjbA;c1n;U}o2Puk<8hHa zAP^!bFc^+_QmvrzMA_!|2HHIB!Gu7Ed3ke1(?|dcwxlSo!vOk=1TKQ|NKJPcmdJgL z<;V&2S@b?BI}VhDs@o6MWN(+Rq%)vgH&N<7mXe2z+h*it$z1mZ7AJ|$ivAz&_701Z zRauo4{`~y@iwn_XYJF&g$xH~p;6?A`;-;gfFZFc`lOO^z3<_A7)2O`GB=)JU`(&OU zbowcQ{p!=B^MQ%BlaM)gZ2oB5d&FTX1;m% zKqJs$;`r!14c-eL6GMKhTe1!&~w&EF{ zV{O~LU)C7wH}7lK|DIqts?s89d0WeW$-LyYZ}=K-=if+3r}|N8E^{|sW?iKt4)c-Q zP!~v>{Ama-ueJrQ#OI+k!R-`oA31u9kWkE?Gng? zg2|J4ys&crUT${E8;OP3m4tnD58WSesz2MJUuw{OaCesM6PyhdHs$*|jn^RW&S#F2 ziMzGYT+NUKL;-1n_~ZZqfQYdO6{*ZH1s~kY+P9d)20{m_^$Z*5p#Xua|GHo6(h{J6 zEG}cx)b+u+6e*tX%!wFU>@2_{^{^-?pxXcdq8;C<_%w94t2E#N7kp*KB8lg$HA6Nc zt_W`J{MmQrmQVC$Es9r;sDQhl0b6K%K^MBse= z`>5RP0-U5&1m1Z(=J~c*Kzv&`Oz-#pe#g6>6-~H;shfoSvmI4phqXb?dT1&KL|)ZF z_#E+Zv3a&YF5s=P{x}~aA*JctMI*AA>(z6CS1WQQn{5#|Y(0!2dAsiNltL}v%%d** z|0NVOJbur9MJMqf!(twtThgXEeOg+_WvCasjSnV|e!Uq{XRjv~}@SocX7l9sgd*nujU7VmlsJfDyabFm$!Vry2j+ zw;k4Kvq|b^@%Zg~p7NCwNtxZR22@Ki)#r`SYg&$cgO-f`H!#OB z@v^YX?CHe+GA*Za&r;x~#Wz8nsZ$4*3)$MsE_Ryz+;{f1A*32zAYMZ`zycD006>Ut z5Td_OT~qxcSplX~+3V`n4_%U&H~uLWR~v-@L?=)|0w6*+JH%PUkTsF_u;YK^V2Ba# zL%d|LQUC%p(wGB2Pdp^iYxVZK{BA>ZnTuyLM+l<6E(Zla0Pjx4AeMw@f6E5(hPrQ1 z^3R)2y;S|&K;W-nZE%>)1lFq*?8(FV3v{*jXoOSHw2I7udU+FW)n&5Qeo)nUNVSS8 z&{qj2k~{g=)I~IcF6UaM5FiEy9b`eA{L)kUL(=Yy%nf%prbbs+eTNT|@4wYXwrVBh zHP@RDcWpoB>6cf8Rt`QgjeOm6j*RpJ9# z%Ce0a<0@i|oK{w`f@1htwTZe^d(@agv*^?iO3vv)5=_i+qnLz|!AB#R2KEWQa5$rk z928!pzA;RdqW4DXFHG56&Z%IG>^F@)Xu83&)XA~1&0}ixvAc@9L)hmz*tdGvW-#Gx zo6{=VaW5OFo3%!!YM!`MaB~qOV62`+$0B)FrCa3Ss^V}(YIN$aR~WnG?aWPJ#*{Yg zJ0PQGmcwKbSjyQs#`TD1V@jRoF_I?ZWliH)b8(nA6|Kr{bu~oeH(M&yt_?$&t?{jK z#+WEYNa%S>g@LXfEFKZKuuT3$LKCK2l(u?W>*sd6d&Kk+C?ax@&nCOD;nX zU)aU89DYg-inZ*LWWyeptZ}|s1Ln(%#+MzYFT}@b^ciB+w?*XB1XHG0%0G4%j`9#p>(+uZ`g!Oa;l}3#}sKE>(_Kch(fOa1j*A-$ zSsF`peoF+qrU~2AcUhhB&;i81flLd#7ARCg5QHJIekRxt!Qk=Um=%I^x^L%8k*r-$ z*14<^Ul%BXeq;A*$8$IU1PR|aIlJOf#2K6PZ}Jfxe~>H|+4iN)Ywwv0nJ4>Z%>JR+ zG}{`5j8HQ=tL+vvcJHCO*e+z{`Ffpx8vTTFtv#&zGa3>r!2m!Jd_QI|8_;f~`q0~G z*=G|`wzFWf;|UAfAvRP|-R_Te>4Vxa*wW zdV18{B0w9Q$7KY^IN1xH6O-DmELqj-n4bTz=nPlK^5*N;KNaG?mmgiuV>-{>$BGk!DUg6~`kZzFB0N#<2rA zW$EL0B;jWEXC~_q^StAEg*B*adwSk5sZI!j_q}fwP_;YO_Y<97s~g>CGo9mkHd&I4 z1)4Bq$+Jd^Hc^rY$s`s%9MF;n7UHh7?^9Zs#qV4?oY{A~+4G?2Ko13@=mG=);?cwA zjTd_t5ZVBc?)ERPbzBCL1nNxrqjOO4$$pnd%_y&6h!G^au{dy7ooFZtQpbEI#z$!) zUs}_UTuUTICv`u^2ZGY9%L50n9bz*`V+XwF!$$ihhpArd6(DG`PqU|gIk+NfL0dgyKXe(nTjJw0nV^y}q$ zY|Upn)zx$37~^}x&U214jyTR|iq+>C#&Ml*d)|wk>w7kA*}d*HyyrJ}Gdy;ml3Ax0 z_km0pDkjFLALtSMc>K7Fzx-O)hlU3MuxvgQ z1QN!Qhz7&$ARv}O3N%_m0RGou@H}m=ECy5%H*ej|Zo{z2qKrfCsdHNE3I>#T8{ z=XF(D)-LX`RePLJ$|4{{Lb#)Zxlp-@3;dEk&9|=kVqk=NivUgWtift`4aCCe`ZjaTMH-%I1>$oH{w~!(rT6!=D0%-oo zAYOUt{NYEqCpRP48$+}zf4 zc?gV#Ae4lGX^DJWLH_sHeGjKXLJzty5dtDS3<*%Bv2Eze~X^ZiyFJ>-dC|fPCMGFX4Tb4?XUvb&GwcdMPpEIbP{GdI|9Unu) zchM=BliQYP@fLV3*YCl&#-$^R;{+C{%F3#`yjq!wQ&lo()@~MDI>Rj4ZRsJTJ%zm6~I z0p#+F>4v{cwzPMlrb&n*rU`Q-`Q4+w?3@g9Flv;2 z_1Q&ClgEejc2h}%yv(u2Y9-MTRYg@*RLs}P{4+F+-gB>u4`7CvfqH?UND#wg@FI#1 z3aJb;4MrNit_}bm10ZBeYFt{x$tSD0Wz_@fbeKK(14$&bF#w5R0f8YPO(TCMeVlQ= zHQZMbjie?F9vV>bk_9D60+!Aq6^RWj4q1^8>*{We!0G#xk_s#02#yrsgvrQqngloz z;@ZRKrGrLvTtz||mRl;hWI!YHEVm|S%?oD&0-rGUe1_=Xc2x+^L3yKw`QX`!M~@yO zv1O!=9LBJaP<#G+KBJk&H?w6OeA&t}ym>oJGBOzH{w2y-;yaCa2%?FRK)tv3KetD7 zJg0uAz7yy#ZZLecu`H$&ddA+`xnyfA>eW$L^tXK{#nb;?vcecXU(nGTyZ+>gBv_a4 z>r^y9QFo8DQej6A@C_SU z_b7x>T;AR?k8@143kiUvP`%EV)5@u3Uv)+(x?qS6jXG6EJ#igQMSuYSga8L0TaTue zm1mquZGd6EJ6FYV(uM9H+}HtX_}2m&Ie)7f&7D65VJ z5CQ#UAOrROm-l}-=3WEj<^^K{1Ci>WoXyWC|N&Y?vM}0xVNI)t^gn;?>(o~@ z>Uq@D;h$4>4fG9I)lFcmp;1Bvd^7Y#C#>wBPm^!a3 z7L=?*A1^pD=OG@W&pbwnIZanR(|!k1Pc!q6P@zz`r9J#~nU4|gPuG3_;dxE?yyZQ+ zBpxo~spJ?NHZil-M0ekK3&osImT*swGzD=YxS3OD*phQic0q{5IMl@G(KS}0J#v?6h+__MV=1 zSlrH6Q@x7GTkCy<7*iSUJ$eBmxYVSQmFkFIW+ZfINN5UyEI}y}F2xh3B&ZVr^gb-tMj40Vel{90s7H}@F0X)H#bNj}L-Pm0`2C+Y zd~ss|l0bigV9d{&_KGN@i%AKpw#a8aZ)b=;EDW&+D>_6P>Z6CTIO+?F1|^q_QSfp!u<6PE7c}SYB`tCA0B?j)kVp{~2_%da1PjkW%V$5e$#Ot|D(d`Y zG#viXrMot=mpsKjr`|NV8aZw6l4p3#=*c<11Wau&wL?J#^jkgsA#WR`wl~Obc789# zFN#_wANGt&K*J0W-5*?@-1^DDSsOJb3^Gzw=sID70LpO_o2sjIE;9_o#%CC|MqJ{r z5rY>oiJSouHAN&q+ilW{moqaEvp92{!*EN(W)7PG@wHasZj{3_Bd*32hyXwoZOaPE zM!gk=!_h^#5@RQU8j1iBfzJ@c5ivNb2R_!~@3qw|lnTm`pkjz_)~tWLR%_4nIb{AW zxb`;WQ{9DY_{Yzes_lv(?~NGaMzZv4iN=j%V~r7snb>KA6pTL1`_Uu_7PLYCy!kO? zz4^h`ppr)0unH-|hZiKNh#O8K8)HcgHrto;BcE+x zX=?pumiN%%J#szFC7v=X3g`jUB(qn+fDgO?{!ZRVGL(G}CDU<@`9;f}5WQ3iI-u#{ z;YU(KqOr!x)&|iBls0!#>DMxYiH*EpY87ZjU>7r|j5^-XK+r;lb@AKjjrHf#u8KRa zvJTwO<@tNm_x-m&3#Z0AgaRporPBqDQ2P>Md#4%i;Cwf?-20GlbH{%|Xavyv;UI>F zH5uvZ%e`_eoOKq~Yc`-=x^7Fs0FJM4MzNAQprmp+WJhJimeL3qt8vxONhgd%LMJ;h zb{BE;KEC+=_peO!OU9r{?y)gQs{S{=YFy2THNhydhPsr-*C!iK)DW4 zh8%!l<@TyF-w%Xr7jVULh3b!|4@3CBf%3^AA7Sf%echf!Ze@1mMUU5=rw)wVWX;OR z#M>OP(cb;4{J*>Tu43dP+V;`pVAq$a49!ox4vb``IVIB_!3~2O1p3`U4b6GPk%gY# zhl*$W@-XxJxDpOI_fE2eeNI?@+ zx|FAmg%RK64_i?rcmuf?la&G`1Le~%^!8ITib@hFdR^!{on5_1_04Zl^BYdHf3org0HAWjtrnPg(Ao%Pu2@wV<%q|K z&kVc!0 zuIeZd&@_V&crI|-CTmqPWN4znYZQXgo!D-~u8-S*PX%&q z+ZPLD4{iup-x44ZygrpWTijwj*u-L>Gze)Wz<@@eMz}dvOH61GJ|nD!$+ zbd;|yqo6PI6I~7CU*G~E9f_K54A%#|dNMoJd%HH1>*>^~ ziZ1(+oK!T`S>7=gHMfz0e~CF1lSkar7VUa7blsum{SwWpx855;1Vg#&SoEv|T$c)V zMXhYUJ-*o@g4-eg^|wX+a5d`sygo${0vWsC7GVhL@%Pcc;nv1X*}fdTa^EgXH-d=O zUR!$TQVU-psHG5T)7HCC^O=M(>;MuCwsF0av3a-m2z*HSUbV^&S!0(acV9Ct$wt-! zB1rn*xB1`TeQ((k{{QY9i>uRfNBC70ji5T zW9mNhSY^kb$3^H~9THc-fz{3e%?}V@27m^0M$c-oD>Y6Qv88HdOsQzablUHii^%m? zU*GB3YPluzUoQ>rHubhasG6@jGl?Xq@#1_Uuld?%pnOf>|F0frT4&z$=>s5byO(gw zkNm2MZH=<+U^;aY5(POY=}0w#&g>LbBjbQDw0FVAAsK=aDTMqjAA-rj#`1D_^aSW2 z;ePj=Ml>2U3@1T6H^+*jmz8rSxHHXn=w@+Cq$uV^Jm?hioZ~9R{L9O4T%4|)LStDi zjjn9#!ujHZs~N^QVCg*0yy+0>gpABUC(sD({SMr?(Ynt}9L=7OTN%@MG?s({8Bpd$ zzAC8b({TE}fY4|YTW;}?yfb@AB#QU{71FjXbfKF$*w-Oq8YG1er%reBFp&#fy3wc@ zj(l&NjMe2mn0uW+hyj2K4B$+|39J&-9)z4cJn=(0X73Ru3S5#r)Nn4ZKx|y@?ggrQ z+NEWFANid!Gu%OIoAL|G{*4#d>oJs2w#HCFIS%NH`Vod=6aDrsj z@SE{jusv2Ck9`vi!6MtMq9qQtj z?$dWl)=nFg9EksF5+qi0p+V-L;XGDLDPp3yQy*x<#IFb=G+Oo?=gues0B^VGmcKIV zji?9!1%*_-@Zk{v7x{CB&vEWO*StQj*-N;Oo_7UD%}d3&Su<{-y2av*L?Eodt;D*N zhY$iKi6OB#g_e>~!Wm{ZZK|5DmL*z(sJZ;5&ZoTWJkA4m@_jBC=Dc5H zV*Qv-x5&A=`SThNi=5;8!bo8lb}9H6l|^-RN+{NBOBE|*mEW+yoFriMPtf`A(E9(& zz?S-Slb0UPq3rQm%Ng}EyeM?@8{Ic`4jJ$Fqwb@^&AI3HT-t&L+nKin7bMGZ5fKa< zh=@uROme>z^ebG#6z>ButRB$@Ysy~;Idbj^=V?b`c@O3ZKF^mol(2G-06V5A?xB&% zqgfniNREP)(O|WPxXXzQOuy}MTjEAec*_MWH_D_yv?~%nJe0X43`g?c&&apoxr3)} zuroklKs&MsGk^C2WCnRK8RF!DkWBeD_O+Z?<)9Spun9jZy}yXcdTE%xby!$Ua)7lkW{ zk1r~oPXnZ~XO}+cj^8Q1wZS~u9IhC-LCdU+p8o6YSvVnx2;4oGl_wd1JPqC#sM(}u z3xyl+G*=;$JuDbAfZ4Q{PO#lo!c=+pFbf93)HX9B> zp;0XswlQ|MHImI_x1mDma9*0N|J(69q)6dBjQ6kp+1{)539D1nmis|r-Y%+|S6NtX zQ97n^Wo>lBhv*Ru-A~_fg@(l%cYp2WB!prVywko^-vRq;WAn$V-JR9%g>j~Z&rask ze(jaK=TF5Z6aPhq=3UH05P^We zU@#a6h!6l60Z%<>wDprxbQH?0hnEM~7>YD?E8dM>sOrzYhg#cxKKJk+7HjLk<_;@_ z-1u5XM`s7Yg7tfdN9U3M%M35y_#h=Oi}pr;W~@fCM&hFr|6)RSK`_BDM2 z`$)OS(KwIuoc>Voun_q|-sUukXR{gb{Xfn6KjZpfAF2CoexU2uy;nf*xxrW5$MAD|yoF|C_Pk~Ixh`Md8R7^a zOk`rB2rfx(TZo7oAYep9L_#9ge0fII;$Aa=VbaI(;xYypaLY*7UP$>qYJ^5LDg&^; zuT-ijoq8x&8tY)ek(Eb2HZ3F;Hn}DT2_3=J@MQNY|u$9aHRL$C4U62 zoC*H&&wIXD$pRVOi0ePVx~|0Zo;z_|^c?68YE-o0_!06{5kbq9989nplc~!ssT8ow z7$KpEXsU>Zo_rKpc=z6Cw^!W&#b@$rv z=hVv=i>4*%Q^h-w;I!LfFk>M=iJ9v8*dXyWoV18Z2E6e-={1H!bQVN%7bLM`%U``> zEEkZa`(06dJ>w(OM~qq*00(znc)WeYwcK`k2WKcW7=zxfY^`IGYB zbMPz;{v50p_DTuLiOKncMw>A8OJ^n2)ld(PGq?3WckkDzUkrXkds+lq!X)y`w#PK% zsqRO@m`KyL8*13}%+&OkjDN7BnLe6zzh43Z9uCe{Q6$|njTig~6|{yH!|*WPx)w12bv=hkA%?IV6h5vF)OG} zL_Yr^#q@Z#+m|)W=lJ1xD>u2u7j(T--Tva`D4!DX*1WsM+$>dZHJnC~W8d0qN0P}8 zzJ6`4Czr;VWDqcHaWuyc;FjgL4j>2`P=rK8MVp%>E`)o59D#Nho}uLBoQ>nvt>m6<*Z74qOS{HL2aiQ~HY zh^Q0&%yG`NE`%&|7t!FL<)}G|aaG`sY0TECZ+BYum%G4k7MCW;`o2GVD_=$PK-&Eu zQ^n0!@Ef07uE+ssu3YN|rh`6uS+SwfQkOQfXB!)@$5xCPi)07l-U8N&VW0Eucc~OB z9#wb`>dek3TJ!zU@=Fk@yX8zi4+`bF`{2IAWx7{w^kXCW%)E>9ZXmSx2HiF|3Czn| zv}o;o3P)>;cLuSlR5{|p*!Gncdes?Y--ZL-W=x1HCRqG|hzNxAc7%wC1Mw0$a7g9; z|9IKAFMGxaM+E0_pV!pid~R!N`L+*x#Pa=*BYWEYzb{Urd-+3rHpA~5`Jy=TX7`fu zU*M|6w5~*gP`9|`|F>L{0U!GM;6S`z=l_q*cKcr!cZJt?i@O>WM|m~1h$58gbw49k zP;B`u9Hs!-z_sM&Mq}V-5Hb%e4q%udh+Jng1ZGd{f&!tY+ZZ9X`>2z|0nF}4zK;~! z=rH8_`|h0-Cy=-what9SQ)?Q7QT6%p_y;0G3nz$h;AxMq@htP`%OkLB{GUZ6gt;8` z-MW1a;YYja&dS(Qj{`BFehj>k0^{`DXSeb(isjT>KHc=#p}xA;Q!XUu>7BHwGvuCP z;27#=158P1F`d5+!uj}Ry^~q0Ja%)6rM<|{tBJ#I3Y%%p z948RJ6|h#ttnvYa-c>70{L^+R7dqlaBx6K(wMS2Xqolag!$;9P{3sA}^7)@I1PBkD zI)`S3yoihb%qzT8)JIENfJ8d!aSx9;XAix4?q>OerEyc-QTxiqu6G+{FxM??zX+Xk zp7?eMi;mav=zt(Zf7dD8J~NeQid->5f6(_J0szo_6vRYCX?|{00IO; zfy|RN6S~B{1sVAf5F&uH;8QB4o+HA4H}F}rSG?t8h#-i70p4F-9iR2V<|+Jqwe3vi z+De^ph=@;&!%vY#Sp)=8K`KU3f;UnOS~dgMH~;`590c_~&T2@*cDp64N|^<7Dd-^hjdnL4)}FUQ7!V{Ds)uXifQe87dOy_<`b^lEOQhJHR{&3StIiC4(cmb>#=DvCX4k0ojRgVY!9D4g8@G<#~GEzhE1u= zrHKP4>63p}iH>g1kcBz`Crg-sL=>o>tgwg?5P{-2^*P`W1Fs{wjL$GT<1K#r^}v85 zr!eIuY3MZjENwslY`|U)BJgs7QJYNL0RD~*Qn^_}noA~O``hx4VR)(aLNm;RvJ2r{ z<_pr@)~L5zt``eMhpN3St^$DOA_9pS;4z(u8NkLcI1jr!kEb1wyHvFuUhMIJoUH3&3L_d2xK@7b2*C-de(zBAQ^yB_wXBEJ@93fUZT#| z&3QgQ_363IoX8tH5EUFA|5z&=m6p0}shO~NKDGx1ZY(IHX@pz8J!Ka!x5-_FsdTm~ zEtce>g-xb|`_>q5)v32dkzo`liV7~D1waB(und9+0U!hrG_OA7EL+5aNr#4FJOoq_ z|1;akXFh(T(+jF^H9ZU0f4KiEzkXfXftR@p^P?4>rw8*ZVhGrgL1 zz67+7@yE!}-!h&i7S6(T+jhsO&7A+oI)zb)Dy27jTxGaa{M4$(WTvH>qhf(l!w(+H znumI8zedB#_qc4sMFI}iXc1Kt1j+#}KmaB(AP@rDMvI><&eCSHnPpi%pZEtx_QgmD zz6z4guUZuSFOh~IE7Q&Y39Fl5@Z{*@Koo*x5f}>uEDO*ifD~B3_G23t_y^1oP{ot= zrVqU^U?BR!Nhrv<8Z>OtgChoHSu$jxqb7|SGHj9rWMvdlWfWw9SR{f5Kr#%LV50*J z1_dJ^Vp3#Z___uafF*$MJHD7v)6-W+vW&;$sy3BU%O!d3KO0C1$Z z;FWE+K4X*C)A`RDGu?KtrMq%wh~v!T!f7-z%#}}XQ0I9ynVK0P=`=({MRU8HG&Tso zd5(n_1KoD!_p|DH3(kox5U2X#AQ2G-Dr!8z<3Hndtk|LLI2%(s6bwXpJFhNW`yGtL zx)J~G3w8NdS>|wfEx$w0)gQ&uivi&+D((WQo#e%!m1Um*i${FT_47&QweJk_Df+)< ziMGw>bKl|Q-Y2)kCadO@s$c#xUF&`RsR8zN2Fp#yY5&UFdB{9BYwiq-l)RI4x%N|6 zT2teqm-aCR31t1-eTI*R&7kBiiaw%pJf$XRh=BqXCxCzi0Y?x?`@F*>;2mkGM!a<9 zJfYMUR*Hu>p==fsNswUxK}VcGpf)1Jz<^-M6C}Vv0|dYc5kL|K1W1T13uIJTN3965 zRFQC?Q*A62m8qU92J?7){(?jZTF^0Yr5)&XfQW!CcRELCga8*F^&r^?GnxMU*hq*N zmMMo~N|>QtQi|!ee7~y6E2vS3qRV0KdJ5xU{5fYPPr zMwmEMz2#d->JW`#QDN$Y?%Ypn)nm|a{_GKb^WRt?K#7dsNAdK0qF8W6+@E<=ZU)uo!1Wyx(%3Au)P7KgXx}sX+bRt{cVq~Y`KGZTP z2)|qtP1Xmwt-pVhhR5vaOxaL0@tf2Wx@YKI#GL<VazhKKPHn%0aL{{f|jiRil zr!%$DX3fC*ptT&SpRb3U?~y!%`RPI0Mw})ldH#R#Pkn>=&!5b5pc%V2J^DEL{1eGb zjZ(cF!(*dZRBQ10AiJJzv+wA+iiAhiBqvlPGO5;p{=G>V=kGOJd3X!D zb?lsMUThi%vyx)W*X@=)&wVYi$wP6%=n}=%b7Q*UmJc5Nsa{SJ=a`<~G45N%3Qy=Y zQ5x5WG4a(v)&L(YST-C4$|^e4BU!U4p1?E7dAEw&BdrLM`$#FhrYaw)6)sg{%hUG4 zF*%9Nj;E$aM+@?N3M{ zefy^y`ZK(UR_c3W{gxXi0lU|&>o{gv*#FuT82z2hrB|cTb8c^ZK#O(7s_FPwIxBMd z-g>Xgx)>}6)Cb4gQ7v`CmvGrp$I7!dVFD}JFhBwTsudMEutwc%H`)GILFQHt zlTzU?|C+1!F&tQr0lV6#YBb-=eGuPkZrsZBPB%!onavZ#w3!&@RrO8n{F$7HUQV&*}}VlGHsP&-JBI zcSheTA*QbIExFOkXHhHvfa~=WKAcVgpn`Bkb`tq<~=_VG&k}gR#IwH8_HCFc54P z-1pV1*i^~DRQeQCPeDy20TJdpa)_a+=O{@X(gh+!KNkz)R<2Yl6G%?tIJhzgWax5AW>u`Go-o#=oMLQc`F&Cup6I z`aBv=%rKq4V3) zg(#e)v2ik+-L^i{wB<9JhRZ5jn9a@2SF7VC-+g?sJ)^MUUiU3hKn2J7XAr^7u~1$M^_HC% zfhrSDfBN85kpcy)&LF0i>QeF*F*3b?_+wfk*#5t(OPGBv24~@AgRjHZnPuLsf;cGN z0%HTGtBR%gGP!=E%kR$u#{h_l>$Rx_02BpC{Au=g62XO$1POVmMrViUMO%24Pg+U| zH6#b~{-MvHngy!Mk zUVwfw5Cg$I&fgtAh14Dd zDtwD~HNSHY#863E<(?KU8!a>PFG~M+g$C{aQrmKz{1p206KH32nrXt%I7pSKG6Wwy zkVWhDh>f4O`EtHCmIouVa5E{7$7_-p_D0x`I)^7DdNMVX5Lakl^nDldv~G8OXRV}4 zV(kt5ldWqm~IHp+u0AfK4DjRa*Kk`TbL?gVZCaLf^HovpV za6|#M{oX2=1okyD9nW$DR9m1uo1a!5A4Q+?syXHEH%6fK&aY~mt69}=qgX3aay01q zyQTaDQ}r?c5e=iC(F?=2cPBOCspxZgmjpnFzvaI^{R>N$y?UJrxkll3n`{-|c+Ked zG`@EqY@RRlLk>J#Rk|7~U3RO95b@G2+qJVyS&-Q@zjPbgWE`0fAN1w5f22` z4%R>r>wsV(=8^;y8M~B;3QL~r!;vuZe2q-1HeMk{4uj&?%ZEd~!n{Wrpk`m;+OBhY zdkX*vANFcHtQwBR-~a_+h08Wec#b7PgJcXK0T5y#ObB400dM{~u80UDodm>Wh4ZJU zT~WDrl*Rj_+9<2hX}4?jon^!p9tD)ss6cUtp3}vA7dnZ*UtjffiwXoXRaDanl_Z=% z2AV=A$7yFi?-a4n+cO{5d^SW{t5H$yl7vd>?qGHBa&_%2a1{OKU^P*zJ@PK=CpTb7O;jQiIfRS&cTU;HsPx5T7PqpC*Znfe3wdM2(XNqiVy4w?NP| z*K^psnW`?24niR3H~<1a`-Y2x6R-dRPWfA*r0?MPY-{Van!gm3%?!#H(;N7X&M6b@Vd*yep`@?z9r3Y5c$rjEH@|g&SJ;Elg_6q(5 zENIcEYGc-cjQN~^x5kW)f1Bt%F!uhg_S<>vRt(BUab+a;SyrjEr%vZfQuN)()i=qqWmbBDb5+A_c@mMMX}8 zVicaj4`Jr_vH~H#^w-z4$;4+giQsB|HEH2H^ccrSyqAYu&CWgUIj@Vm8x23&$t9N` z%{6UO#Zm9k;~@Ea(U$v`kNIz=nr7V>huV|Ln{Of$F{W3gbpJke6#0F`5alF+vhgbdArwN zrLBH7z8Z-TwJ-n(4O;0D0tSc?0Yk;4HNx(h8A>1m@E9T?9Y~o|RTsp*dRtvtvg?ol zh$c_BS1S4d0CI7y81}Bj8T(HFvp`J0kNDOIh=@y2fD_xb<~5HRe;wkKRl4+&+c@)M z=H{!XAmu-{Qb<31$Hm9-?|I;F=#**D5q|8|fbROgrCk$kqO;wlucF@tHsL({0Rkg{ zfFMN^UX1G*z@lKRLa97vrESeZ0JfHt5$Q zF`DpBeRI0;L44;%nRCz6d))o%2tV1rBhL$e9Q4^cN`HexHKTK1Sn{&Pt1fMjPIv5| zd$-Cu6l_N2!SCB79Zf^w^)^cpOVxN3!76o|x<#`J7wjb# zAH;@!Z%n3HVemh&)08(7M+;sDliTSROQP`*D3&nB!~b_N!O?ctP+hmR=`q~sRR8U$ za*A`g)Q8`Ab`b+eY&Z<3BORtwh`6K@JHN*zVCDRLpF;$VXT8D$t-0j;zYA-R*cA8Q zVFqA98KdMF2paA6pCo8VqiG>GGFe9+eo=`N*-TepW#PGS*{02Y zQXt9xaYwb{VeZ<;-l4?y-Ba{CDv{HU0D%Ci$a(WT&+c-LB511qNB=#GjqddpX+kn5 zH+N>^4la|p$KTJAgHo_iZsX<;aLkyw-GaRnQD)Xy^KoKrI za5Yp~B)rDIc#D&Mz^K&Ga_8--qHJ^Y*CDF6W|ScRiu3;;{q%&b7+KmAm5D3kyObrI zNAFVHado;&kOh}BzNf2@WS7nFqJ6i`sj<+nf&5puL~~5Klaw z|Fh&$nz|y3e~{ZEFT3Q5xL~*X5)uwTD^JpY4R5ck0D%E141Vd>=2I(SW)j4Ag(Ga_ z)ySYnXRL37hQCN7`Y27PTyuOsmvb9SWQTW)X3UnA_upI77`Yzz@Y^#Z+GourzIvy% ztIaFX-H6Sm_M1A0=3<&XN?+0uZOwoY0?N?WJUtNrS3nRb!kHjM00h)T0B1ik2oOSG zA|fiOm2;aUx72v8MYTV#R}BY9_LqGU>Uz&hS;Tt1SpfreMJCXJ$@kyQ{1YQG3SRM^>t*`)v zLz(8Db9=9N$rNCS5Rp;JncwqMlF#6IYPB25rJdy@o4FASr)A<63P;9>i>LtrNeg~Q z{VKe3Rm}r=?_>~U@1g)efbU>q07C~ACvkJHlU-qA&2(`q4|^GP_!NPcU`|PNq+Dk@ z(FEQ?Ce-{8E&#ez;oM2V$=03pIMdnmEN^?Jnq16^%B*2bjpN@Kpx-FEfdor&az6zW_gEq4j=$Pvkbgi?F!;w?)8afm5;G7&D&QQ=kA>a83OzG;v+I&Bj;M-9A|hm zj!*`^$LdqI^*-(56B4f|ilznQ;n%N$Sp-5u38T#DWdi@(d4hP((^2ow^g2D*m#3GH z4u2lv=n`RoToCcxDDnA$&s&C!u%>J0p>S@^+Q*0wgZh(+tn`93&f7B7USA)XB8?ZH zSo;n3v+6J-cHR>C(UJS@qK0^I0|o^v>Dp}@g=_jN#VN!5B!31u>ZB{8U!H5Lp@OTf zQ^r|j#OS4J-quDz$+qbJ29Cj2PN`wM$7=DlU3-3!HoU~KC+g(!rCCk-7@>!7bTyPv z@s)a!uzd_|SC^z86txa}5f7KHV~uNa{U$p{7U0&WU4pnqyZHbDBEDmyDUv3HYBl5x zwUjj9XU6{DP%3x6!Gx76jy+xzrA%^l0$%?&bs z*O6Gx)N?y9_G9WTA3-WJc}qUhuUWAmKnaOJK!{dW9UuTAcuKs}=G7zb<)>H9_IP~v zuO#MeLp#51YO?Md#Ai3bmb$rn&m5+L%0+yl<9Z!leK{Ncc6w7x)~@2v*E3V1Gfn9; zFR)y_LV;41Zh`c4{oRSR{4y0{N^K=e5#IHNpb+rxHb4;q-Z(?h07Ck|Nq-k}vXyVi z^Z#Au4CgXGr~3L8HZfdR4ha~E`aF2_r6FZ8Z5`!zD^_VXJHvD~UtbQ`7l4su&b)BocFL>1nTr`0}# zJNJX4{X2bfY$@H9A02F}-`=;^)ikD*H(DcRVl|s~b1h1pEx+pQ^*WOVwhPF|t)wC> zJB*JsPInP&X$-BL;_^T7IQ-SJ_>?@af_F6o<5J(mBEs9?D}Y4*uEZgatAN@X^4Ocn z)|z92xe zzGlLTYgVy5s3d&0*zS~ek?HJ<)tKzX8@erWKCfcyT$`7{+5> zT$mP)yGp({1c=U_DX3x2FN5P6Gujyw=;{1fDh(6r-OC?4RJU?7wGsv1$4E$mTaCY z_fYvhzN7v9ML|2@ZiI5I!#mn=)vA5rc8m4uFew*z8I~_<)k{M9X;C7%sSvlh>cVf| z^mEixKC_6h8G}CQd{S}^6=+luXWRxF77_(MZ|c$M|UFa7n{_Q5=lBt=Ll@! zh8SQl!wfSWiPdf<8T;3pr{)BWtbzX>CznUrVsCR6{H#=M<-nN#DM%1rJ$%WUAqYT( zAqZ-JP8XfruvlEivm}I)PV9Ca7AOqTZ?r6oZdc?)8>%;4p>z50@r(Y=w@_KX$~_hi9Rfg|E(c z4LT|0(!LPxpaK&QmuL9upN>PnL;tSyo0dJymDa7BVT|KU%C1lLD>~}@z1*(vS0L|p zf5cqeBC{aavNFQQBQaqg?PlM0ws0gwL!{;Wn44;YK=5}gZo-lG%k_V6DM03zN6*8r z`pjU6iFvKU$?7poS6n6SZL{Mi(IWK1WQNz`$eob@1dk?FA*c(Vc1GNK|8!U?Zm`K; z&7MCYapzKj8?STYGaALL%pcntQsbP)sFbwc$^Zx6nNfrSAOST|XViGy&9jN<#Jx;Q z$%+6F_*!&nCfztY2M?>mB$bLjXWi4`B~h}`|IGOq2J`QRwg)&lwApSG$HtBkeE?Wv z>ry-3b@ZkOT&;|Lv0d)@t;!XWKF<$0BlPL0+pe->_2WCDKbtpcxS7sn-XS5X>MfqJ zd@BSj>JQbx09!hzno)@7V!TxL7wIXTeFXaJGFIx@ZVArI({dNB`oroDP}{gjFrUOs z9hK7cA(jg|sq_wxu;{nY=`c(upFt2HH2+8_9Aql%hXOS*&z<-c2M+Yj^F zynJrRoRADxqf*n&m7q!>4uScey+;PzCR#Uw{uMMgV zb8$?;UATW-#YvKars>N|PQ2YTN;x&kf9Mzf>`ErP16TIZ!TU~Q;alwi0uL{n3NBzD z+&Mj{dZEU#}A-m>KWY zQbu!%+q>*mU&PTPsKHhu{E8mRaCcoVV!R2>?;m^MAkS}K(ZxoRS)Tv_g$r*1Aj+5s zff&8l=^WxdbBW*g6Azixa67UI5_)&JCq9pXgn$G?N~`DQJ@Jukyqxc6j%(q@n1tk| zzCu;{;6uX*L*cLPF{0WEt#xrVsd#H02C~50Bo@8S*)cY)9#)Uc$-6pHR zeS5X4g#e|KxYkDL8(XWAQrx#${V%J9qhVP0{SI6n; z^Xm(<3`e9Q004L{-4CKwq17+csi)t+Jk7JC7P3=gnzH3R?=|WC*V<*kKBf(hr{3!` zfY(3vKOL_Zl;iDhrj^F9!~O2g0haEn(*bFMR>L8Z@Bh4HT^o|Ym`wtMDtUWDq;ZTu zCXe@dtk!EUX!fW_M9IKp9pD@n_ZE9$aq;H1Y>WMm)vBhM|cj_P9 zG44j{;IRjJqL>f?P6**|d#}US^8QxVW4zA$rco`k+rLNK?R{6aUpI~3*#Uqi#Q@cUvN_Ae^v*4!mn&qL#1g|%r%6^pq~4b<|qW{-G3;)drR zV=tzHTF^74)AIRt-?2gfAhh)snTs7$+kl;3S>HZ;^INwIy&3J(TjFCYZM|z#tMWWl z)~|MvdjHSRL}^*jBU0lpb>7U^!}adfIc`@`dhJLu%o*l)z&9(@^L3%t!DR{P*L(m5 z_p=q8MOH9*X`mq6-I!Z(jP_Cgt6-I8H-Cx0huNHgZkU`jc{WJr4q|_Woa<5)vDusJ z!$MHFqM!lTwodzJs87xGn(hGW8n6CUy30wET#U~q_R?lP8fI$d_AVlnRB9xv!ooNG zc8AJL_O-@Y-*f|R6++~x=N7N!x!QLno<36yHM8J>1|k3eL_S17hqZeZ2Ia?X3gp9I zN_u(pG{a5iOd7ZB1J6b0 zeoOn2^M;2@PHZ4R4lP$(hHw_&tLAy&f&@JP04qEfOCnt#i|lPqv$mMzKKNJhv@I z9LDF?oiQtm^?ZYjrp z;cRQvGyI8hV(gX;wT;mO=-+eKj_!LV?7kGQNZvjh0HCK8wb2eI>(G^drJl-mpN6XjBw)Pg=k0GG=V_88iMm zaMCZr5l9gaj|r)%e^vk@Aw>+B2!V4RVo|Ld{ojMBK_xFuvaUy?QRn?{@wuv$01yC( zKJ;QctC>KExMqM1+y>rn>5I8t)EQTHTL+nhRt1<&HZk5KS*ZJyUms5_e^`%{B6Uf5 ztxhX7hC`jaa`zQ^`F-=2m*(11-X}9N7s`2`v*+9`8(uTKr2V?*@763eK#<>{n8{Hw~Z->yLnTnZ&6pDdp|^OYG9_j z@o%nJ)s!~35?f{(1#ddkGGXP-`n9=|e#IBzC9S2PdVzTIy{)Fw@i7y6*m|nfnTr*} zM>P46L_mlUbVU#%17Xy@e(0`@Fs-Bw3s6wiOQb-H9TtJ&J3`y$Sew9uasu2X0yW!b>d{N z;rgnb{n_3_HVyAT2(d2B^1CGxb5sLR2r)@Fn_5ncu=D^)j8qJclPDk{^nKUQzfU`E z{73M+1YNSdNmX|Wz6XgqAKFHk|G440w&Vo?@4cH}I$ts{5GxvW(_nv$5=IKHd7vaZ zMu8(HzVI`gh@8*;dopE3C5?3K>?&`%d66PYX>G0~fKMU|V8}8?Oqciq6(%T;ejDo$Zdh-z_PawzUYiLR3&LnPr8 zOt+#K{LLhH>bK2m+TkI-|64g;{ORBeL_)iR{`qHPN~F^HyPw4Vp+a<3B|jB`vpo{7S;%IQAuamQ z>K$aFKp+V!1c3lt8WUU?vPpXc2rKOQzmv?vXS;1Ze3u4IQgeI2o2qYV73>Ox^lk0i z%(&dIIh)V(I(U^;;yRG1br0Vam(1`QmU_y*M2rpzd)iG&7NUGt1xWTT{34^EAXIYf z+Xkl7{C%BXv;mw~#`*Qig!(?_r)-*hcR#z{gHQrQAIWL<>PE^ZxZekCVBkN1bpWL+|w=bC)6nCh3Ni zpabFW?)K)-QUP1gPd_P|zG^kRhyMS6@6+}$m$p=?B!~0J+Fq|eR~e_YAmt(giF5XQ zRPD9kLyFOPJM6yu-|x$?TA`x-n^~tL^QgvxD1zzFB>*n|-M9qJu*g$#!Rgxh0_&>Yyr%!w9}5t^pKQ`l$#2T$2VSF`YyL8Yv5DAlb8+K+squ zJIo-u6855v(e!+x_8^&`;bw9;84p>&Ym zaPXb1bbtg1i>i+E;}*3qkf-nGJ&>I%LkOqP zFvsc-v!Di;*oTNb9PxUB0`hxElc2Sfs06+*tpCbx%Q2QL}&E5Z{yZ&8o3z- zznG>b}&Q{Z8@Xa#$N%P8JbJ;U~)Vv?={Zm6f!=Z8F%uYjRz4=|&+w*w6 zTmQfH_nWuZ^c%0glFtMqE{PGlXi_|~00@NEZAcLiBB;kP3Ef!HBv$MPx_fsoU)uj9 zqj$9o{djKP?`$C$X#bGh&~x#d^-XEbZy&@q4r)SgB$M1e`CYANo^-q_FFe&|=FZQA!0t5#5)o27mo1D=9 zU8?Kn{2i9?-?LqD4(i+5{G1;%>~~!%Beiq%l7~Wyq-=#-fk;R6j17BQ)-(MkADg-l z6&%H@efLTLY=UssHZ35v;cIc0v`Kr2x3otnFXF;W{RMd&d`18{IT=#(Pl^&kHJF_R zomydOB9iwa{$7J<3M8#I?uN{+ST)%{$F#ne@+cbLrEF0?-~|04@M~!RfGG5=;)3QP z2iMx;1GPPxtCaNi-IcTSJw5m!QixNUozJHg&Yr#@dQbJ2>ALG?BA8ZT_;m7ay=eVM z1N|3;r+?S!m}5Q6PS^e8^_{1}h1s41`U-!&B_9#L>3wMb$hzAbP3lWeIbg<$y`#n? z=f_!^oNW47+DpMRJ;3?ophVGu>u;{6g;wwG_~YBYhzL199KjA6K-pmUg%vLWEl=8kFX4WIq=M?+ch z?WavnS6JIKH_Npz-Zmx!aG`?~MG6WMiK*T;KJVta%xpqBi?)QrvMy5=q&|894T8a-mqJw);L9bRa zpk_S|$ALUO#^<%eU{z7RUIPjmACK459)LnS>0Mxn$-dP{qiE09vYyV;`)%`@#D1qK z^6eNA5fJw_CIAQ}gYz_bHjw>{{bQuvjXmWwR^t^*@yYSerJg0bdvuX+{(jYG;K}a# zH*d4lMsVmzh(llSZ1*iFS1KLq^yNqi$hij~eVPCfBAkvP65Y={rbYT6uG4=r?{n!- z{mtnQEFz)6rTkV-4kSk%Aw;HbMi(Q`W_p`-!ce2n)BW5;UXgNL#psTGCk~3zBqa_q z^`3BCEiH3ac*{3-wz?HpRu*#1Jn7`Y{pvvMiOrQmB)O(A#IAc7a*?9c2 zj;QDoCGr4`ByvXns3nyGn#Z@swo@!63#1w*1PhgKYTi|9LfJ7QsBG8-$@-{=<@)=s zYhBCx9!{y-^*x4G$B(YudkgDJ=6NzanfWT7tB>B#)8uOahyY!$!Y{*k3PDEKGT5Ko zs&-j1Suq6~e&ogDv)T&2UY{dqL_|P?&QKK{d@jDyYPZIEGE_6G)$3avD`jv>hiDoi zA_IrDrP<~(WX*aodzYRjzE|cH1FI=61VD-ol46m;DmOxF(9m?*^s(Sma_JsRg-m8e z23CRTeB$qoQW5(+>xLN4yT1oneHr*oro*G1;r6zVoER%4(WF3!58nSCl?f&%@sZq_ zRGN-Hs~a9COReJ;m@H9#8YX=0wOI3h{vSCKJOu`Tf;CQ2VnApO@sY*exmPR7O2?rpG@iB z${M!z%OQY8uSU_lsFc?O zU5C{)dz<$1{`os^x(sIJp(^&^IFS)z-BCK(PN%%7{^^8Wg9+IwfEIt;H`(o6(~U%6 zR^`n?^_Rj_C4un~5p{jGU$$Q_k7d@#jzHrs?btOBvH9^67ZI;lw)U;SpAJ|6fdvlN z8~{1AZIZf871{F!ar}AEGk`x<=0t64~ zSbLf775_7v(8sUZ`WVFrgvbO6xil^s3JG}jZ|aRm0`@!2&&G(WlQy5Z>*`rMjlI{g zeYS%0YHV~ou0ox=OLM^fzI&hi+TLEjkEQXX8(4_rAss!}_Xpoew*|E7|1=Fpf5)bq zoO*N2EwGwb9oy;f5ZzC8vnN#;c)~qsdIAC+r4kQ0h2Ss*NRFXKbsoRyY-WE3sVP0i z$m7}O+_}(f>8?{BxY<-#ihfsXrLEd9Ip`PO5#V8bwX@ciSksy&(|-g(QayBMtrZrR zH6(|iWHEt`8X4jm81ytWG&D3Z?7Taf-fCEiisf>sRVtN2sZ`sculpG6Fv6(+pS- zu~l-4D2~)ABRbFq+R-sdPd}d2>k`!O@D#kb{=MGmr&jy0T>wsWqx`1@IfO&c(34DX znCFWUsH4KbM;s>dE9Mi1XJHqC^+ssrB0n0VI!L_#KI9+w!k$9Q4u2e;WwZW5S z)CR20ETd1au8``}|C~2SDQPS-9$-X72Mqhq2mm+|KZX8$t&h%Fhynqf`V80vL>2a` zDFM!=4~ZpIaO2ukIv_d3@0N8b|DEsV=rfixNJ>|}^Lb~K@s<2bM$GlB3rX`UD>RVM z^|Cu&HlL&?muJMpOwSn1Z~DZifUBCUbZY)RB?!>q^>_>8l;QsM&-=>b@|~h|*_6n3 zY%r`0o-W0U-0?m)v-J5tdn;@3@i_y?y4Lji9?N9@AGt*QqUA)2crUC(gkcg857c;J z1t3BMAVhxf9B96Xwo?M;Zq$6c^^?sqtOO~+8VXvWeQMP1Yd`Q7WnAj-qotk-> z2tRj9stE7)>r-8rJa!__dgS9`vz_+TdfHz5uXeEvzP9&6GI-R=9o#9{zieL1Jt~R@ zChhTwv<(M?aae45c8VUAi zWDfBF2oV8Z{~`zvT^5zU#N_DVyVfeJX><}mz(fo9{zO_$0iq%xhyVkHPkmXd$z;o` z(-Gd}d`G+%(YIKbz*q?LmrR6x^uU}iS^Tzs>GMYFI%BVmDGNI&+_u~+S0DhuZZYBRC{V~G zy$j+9*KfJtuzS^{SXPP#i@&WEq#Wv~X6*H`yI3UlaYwGq-}<-OB3Lfushx zfRTS+h3zSE?5BK5K~U9N0CWI+BTNj5^xB;pC7jzX)d_Y&Dt=e}tB(aXh_I{DKEo_) z0r-KPibI{VY|3P$Umm&K+pQ#zjM9}=%4Hn=j-83?)Z3#C9j^ZqUr#|9vnN zouz|*J`#mMV{e8&_z#3xZMrC!rYA8_n%g=_NhiUKr`vl%qW3 zUxY+6B3<=t{8tTcoe=&3gcbs2mQOMk777hr&+z4MU#b1jN9E!1IVq?Iu|R~9IKpQL z{E4eK^&f*D5#a5&o7=JV|4wvC7s(P5ktCOa_(6@lNg`id7ta<;(2|aV$p7$H_^}YdHdd%eE0dSOX&POhw(*FPvC@%gp7oYgCNMr zf}n^Sk?EaH$x%v*iq^8IqLQMhqLQMhqLQMhqLQMhqL73jMHNL7Op-}5NhHZ6lO&Q% zl28h%i_QKU;0l6#;);qg5+sQtNRlLqA^{;GAt50(UPkxXd)+?6?q;t#F89c&-`+R1 z;CfJVE3nS(^2yS=J*KR$kCU%7bU0Cs&A->W}i{#$Z-C+1D(#%W9}A~?xEzU!ScPeI6hABC%Qd`myOEdU|} zKo_0`4Q3D#5F#B;YZzN;9PQNJV)_j|a%EylgA+5xehkd6%hg; z2xQP_UEgAgS8les7A2c`hIK~MSoK+Bd&*^WeTE~v^)FKD zM>m5#3M5ayjj*tjhQ5++B&=Or+9$Dz_}>>2CcuV!klp{)a!a6Oe@?!^R#;C{=#Ze) zTMMS5CORLD6-;~x33s~S#s8@*p3u6x?n>M#_ zOWHFRCo+2$#bU9|;x$I)$o|H9w-KNG*g5nPolc9RL z9nSlyMQ1OOmIXz*hv&e?yr&CPP{5VcI5EFeGuubiBa>$4m8Njc0u zD3B#>7uHDFAV7f;5pMJhaBiAbLkkm_9Oi-rR@lFx%l8e(ohMIZ(Km$II~PqUZmH2X zN^~B@Q4*F(+FUyghGJLZd;lr72i+UGgEJCmEtzIMjNDr>*seLl%P#Ab$8Hdlgq2IQ zslZ9;`rS{S8miVH5PV@(0u&H13;g~(@hsM)b**ju)`;Y-uwU}%j@|l zj^xvT6E5Na5MTl?ET=~9*DKEIVdbtZ&m!%p#ccPQ-Z$0l z@n2R~pWt?UC?aAoGglYMf)rXp|Ak3rLOccl0*$f?HQ5{>K=_C_N&$qP>A=0LbRYns zf0S1J^dPk#3qFkB#buS^)WASU&wKA9rwI^}?tW4A^bf1xyx1!hKk z^}b8$rL>1xcF^X#pDAnk1?Y#ke!LJ)t4&f+Z^YC!SKDutVB+Hy^QguyBh6-{6QdXg z)Br>X5ns@;X@CLb05f$|owZ1WDs1X*BKk)F_|(69jltt-`5)R83}$i##%! zqwMV~6+Q5|{DqKOa06bolNX|tMWIW5y|j=b00P^4&}y5|b)0a&5;>Or^ph!^Tv%n@!fzZ5zI zm3xO~Z0_@)d1IsC#!>8X8s;|V?`vdP<^N}K1&I0joK9!G=;chQ^z=Sz25i?>es{8k zv6Ii$3jN%!ic9eB71r>#-~d3m8Ln<;1c(s; zQhLKfh(;3`0tfs%$_%^TDrtcb53H!(e$tW8zYb+bfDsKnq9DyM0McsVd=COdP%0aa zi>uRHZ5i<-UUQGd*MF>sXW^iNetqkc>~VmD+X5l!>U+Oa-Q9S+M%S9j^S*`%s`{&~ z+v&P(xlTsU+K6fhf}?}Xem z*bQTX`yG#2>3|3mXvj@Kh=48W=N@%wTD!h@s;!;b_cyjZPZ#A(IceisDs|8H849Yi zolZW{!3=y?O$2W15v6ptT!+a@JAeVi*vUStj(c``qG|&O)1lc>eCJ-K;_}8q?`l~wH;~S-!R=gk~mC$<$T0%cX&}v(sC3QtDNopwgJsy(dfy4jrOQZO5 zzV|1$TIBt{jHzu5mSs!OHPJbfb?C64Kcu$n`lk-{GA;9HA>BJ)U7t(>08*Y}Hbg)T zOz`q%PN>dgOl>`)w-6Ol%9%mc#pJ~OLVhryRB-yksf}_;cuRS+4qh%iOs#6d$%vMT zBq$4_=;|<*sd|@$sv=?c)R86uf1%_=T$EuIF9Kilwo`dXn9L;vE|B0br9hm(kxT(` zB0^vhfJF)$cX5-his+$2?30V{crB~8zyj2@G0_-QtV+%o6M^%&Jga#1c`fT1*Y}(U zHH5}tInH@ePwz#2L2*?G?J02+!|m8JOILZKW3sn$UEW7xO;8QGA5}~9VP4(Yk(;%C zaNfj5$81MAVsPVKuj3vSLvrU7QeD% z2ghxuSNT~e%kuv>n3e3eMcSg&`s)j zR`)!8&$;S&xqmYKbI@;D@9}s`2u}0_Pj9}u<+B`5E4W)0<3%v!QdUl%!ELKc=aG}X z#7QN(Hxx^Rlu;$B;ZecsSO`V8sd_nbf@H||D;0-}x8%0pgVW)p)#eLFoXvAjy3^rh zSJ+=7g83Lx_$rQUFu_}uO4ZyOSc}x4su4g`fLaa2I?c5%6jW@a$sEDV8xU)8jL*OO!c)zn_DI`b+P8 z5Dx4MZf8YIQ#;f1hg~n(-e6Vlc5fKU53^oMJ{X{>XxTLf|06XdDnPTjG=m$gnbYWS8D2QkcS=|h> z+;N(-6R2MQ4hjvwnk|zT%t+h0RE}9K5onA8DoT?2TO+TL zAvlw0D~Po}Fz9Fg4Io|H0yPVx`S1?3G2%D<8f7%OYNT>)o+(l$nBtD)p zQN35oYnW)~+11ZyYw{rHrnC*p%5s_6JjOzl+H6(kAKc$V3l1VGGP@5}fVuYRZ<1O1 zlE_`3{dmaf(|O&{x^8nPwGl&B!^tAfh{Y`YW)9eV%^BSI>9v_wG7HW3Xc2lpEdv40 zMLdSsA1$Qo$h^*1ukMbq{##fLleB(1L?dGtwe9@*(Vdg;D?bk{R*!ie$d;A_(4i6_ zonPGK`qW9bjP#L=EsJ8$<1X-dns8g)_T#a>OUWgz=3e*R;9U+Fji00FHM$6QzG z=e{2OOTVt>ING$c!LB{B#`;7znWG(E?i@r+<1bUYe|(<@ypCzs_P2;NINUhpDP`3J zHj^jC(-Q9EbdjZsPFmYo{ib+&73jL4m>c>x^@*)jG(x%WeP{3Qsr3e@0Rf}=!5ki^ z~c^hPA*LefXER77k9lY2KN1>cG230)^?c z=k7N=pT6~T%a|?h-$o)dX)_Rwwk-$e?s!gb;s1cZ?Z5K(jnpB+&@g|nzS|e$8e5h$ z>n1$c)*9|r`LY^nYmnjY^4;HMJF=hFr7-%?^=(hczmHY#G3&~*m$t)e(RA_~Wf-cva60F+CJp$%?)$!0mZRB8bTfVvWcEJ()>^0!8wOAM^4f_cqgWn+0%bFZ>U2tx zfvk&#$P=lvEXd9$9@a*8zy&h|0RS^4UudqR&|hqtON**1$U%c*j(ajdwZ~hxnnvC905N$$4&$R zeLCyoF!C}^edO;HW)=Vi<nURsv+#dLL}oe?ofGBB#wzEz8Wb0>G#MUM^)#`(h$680om{jL)y6ADC3}wvC-HmA)IM zb6>N*u zKQV9nbY^DgSL2nv+l!=V>xX`U>Lc=j;u+`0A0ZTh00=e+)dTAXeWL(9EKOT|>uTydkdJfu)X6`t8IoG1MC`P*(>*Szz>6)--Zf#~qqL&Fh7|C?F} zw@8?A0S8T^-##J!a0QA*0g!|t2tp8$Kv4aDPYQaU3W>9#FB|{Zi|@O@ZoliK`a2&V z)BAI`cYE(z~ z7GEqH_J`Vu&P-pb`a48*`{vP3VCC^!DQ$2dV?TOT^hFT@CVAiz&71Iq0fVK9bD4|Cnl3Gc&a z$8v7w9Q{57o)RdFNHB zySz(;iI?+pDN#nWFQKD@r@x-`@d5(I7hBddpTT8~w|?E9qtq;7aQXCe-c!aZrp3BM z$#e(KwpJoUUUI|43F_{%6#1GP>7QZC)|;kZ2K5`R%A`6G76f4c0ipy5Ee+a0shYH7 zJ%4B9;j}y9Bw|q@vbW@SK3l`*v0CrL5NAAW*L)OC#}_}G^<>a(<9kRJW)d3#t(&VG+cG<{w*e)^@`-U~g zNH(<8bhEZSHZ#tdydF`(X#^CyANIA{q=WROd90*J-Erw;9gn^0mDGSEjwFsl%5<4E{xj$rdvj=D zmB303R(c=hA-1)Y4VH`PHMIP|`{%7I&~~poe=JM#dFyNy?lXENcI#)y*4BHQ@-X{+ zNR@bNXKjEmkhiV2u4hnHjQusq$LjPHP3k@c7*5 z1NUSbw}n^Augc1{%(|W*`yuCES?wccZ5_cD&wIa*>uEN^MXnL~b8)@q{28|p-Fg^U zbEd$9;V3N4-|!D!K@^}K48*;M_18x;<&osPcHtR800;t-aDmvtQ8Z`uY>_>cDL>0x znpfjoK&+o&>A1NtRi12X50nH3cI|3GF^)l{MQ6#yPOtm_NPjF$`S#MVdH1AuoZ@lW zFU5^^Vh%c6YC4(wKXdT9~Hyl!547(rqgj+fbozN)q7{YwSCX` z_^kk-0p-P0Dl9qz8?D%ZNGNx=y9xO#J?J7?l3?lnaot^k4_d|5VG2|_oc8C;*@SUc ze2$L`s#ydcg%lsX!Mq*53#FJ0#}XYk-&9*`CJ+@1v_;DJ0-~JG3fgQoMJ=|G2f5}w zJx0iiCb_p<`=z)Fn$45HVyJfhmdm6NMXW&2sw6}Moc$R+%s@F0fB_V;sxQOo=PpFf;Y(NNzh&auU4|iK2VJLIa`x`Q1u~g@wmz^&nuvjs z(On8%0JZ+==u`-yuTdn3zp0F68F!@G9{R$|z4;u-`%5M$y}cGV}lzz_j<>Fdn224K({0kDP<7#U>z(4GUY2=wY* zrj?^liH&;zb&iJ5n?Lf5F#Nm8M)(W zqaf@63GT_V)!a9WcEguRCiPT5)06LcD3qskci3So4sHNO29G zC>pKy@iIPRs;^c$@81mxl&$37%R}=2lgI==$@3GylwT7u23+s+k zduz+L9$y1r4G&<>;%ex*jCtx3NhFuQ7o*?J{NK&|-_N>dKp!C&nOo+lT$o%i=4bt2ZhZyhsXf}Bd(e~mi{`{*Q@2rHzj_~nv1B>c)zsCRI~I@2Rj&irTtF4 z@Jk*wzxs~#e=KeuU_b%@oi3Rh7SnGICgVIV9{sNHT^a~Z`a%?^SxbTw36PLZ$D+#R zSyRwg4`A&CA6kWLga8`nc2WhiDP-aVYj~VDlc0f3FUPm1(yE&=Yu>3IlzJI-zMlm2;}3^cXWFrr!|xm}V83l! zh2*%S&6pIR00@LcTm})t`-ucZ77+pkh4a){wFF`>vl9~((3UD>zXU*p+d5ia=OJrr zYjJULVPRomVPV{05j@UA1;plcC?H&$*~WDgzRfy;_CjIMHaIdJohx3m-OtLD#mv|} zX{p;5PSzo~vKZ(^5F;p<-i*}a3%z?+Q*mMEWWnsa06+pDit6MBv&U&s`rq#3>rJE{ zTC=IKRryxIG5`kH03s#dU0+6FZ$T$LOvlHBSS7~kC&PH#|37$JMO)Q35G~aoxA0W& zJBEq*`HTZ&gzm1G%cZyu+LTCGtWft~VcNoi_Kpror$zJuUhd4IGK>p{^(dLFc#TD(1vq zc8N^mAuS5EE+1+x_zyu31;E6~#YcWqauQ6B2004coKd^O~3}IAEiIFl6ojP>s z))uZyzpsXx0RkMI45{=Madx&fN0Z|0rB_co1FKBb->uVLix>g~MU^-;(Q3HvY|$!c z*$qh_s2o|VM8#HLTwlE6NcciPkOUErPkI~<1uhh}X^w0n+9Wd?Z2A!ZM74f?)jd=F z*Cmef)5|{}lVwnw)o88142L66&QAJzn~XJ8BqQ%)9)ezg*ql~QZ%X)VKc|ZF=964j z*8@M?}y@O!)(sKr*|sMab;2ZBX%k*2~;#MtHD zVlF&#eDq)dGVIL|Aj<>l)iN+|0XHqw^-BN0z$seVw`aqrP`J177B*1-Fb00;u5uOI+s>cvnXqvFKy zPQ~CeZKUH5kZ?@}VICL+cs&PBojP>w?r4Y*%Oda%kN_ZNW#%u_CrXpoIUiWmb1bQX zl#Ct$fQWIC;$)|M7Vmy7$pAz^5T$C>l9ojP>vPI;bl15-;Dte@G40df06+>QE-*mM2sumFe-7O$5V$5#!Y zz(f=P081>&V07%ZPrm78 zBuceQgz$P7%aMp{Taovynu_p1rsnk>MBbb1ClmH>0{cA;S+|%}q47s`sFCB5R=wXM zA^;G1qvC=f2zjl6j(s83FWbCOAV`0EKi)_;Q5lD>bzH~W=lGt()X8bu zewnd+b~ztvi^6c!7ivk%QfPeIhWo()ZtkJZ$0lQSGqt@&R|K*I2#AlKPjq?<#<0t8 z&-8woK6VRv2%k6Gz_m%Hn$&11T65pV@**JXKIAcg0D&|aNrr;nquO<#_TjK5;mmLI z4#$hxhR8M;hB`iTeed`vpZAJBJZW3Hf0s|u*TW@2W!P1v>H+|+brsrBKn#RM_HZ*` zYCL%OPM(Y(hY^K~iTBwr)%BAx&M29#+2N33FID-9nXSLP_@tQ?oJx7IM~{^`z|&~Uq+005S+N%)fGqm=ML zaBm>6TbO$@7d>~xu+A}ic@{Mx#1J6+90&r?z%g&yl@YT#k4_?&1_y@^x7=)=;BQeY zDx8qjliQN29#5yaF`3n#mIIesc!2;J4-XZU6>`GZ<)+rL(pk8j;qNOCoU`^6ABVx#`e0;oTP6EH z+23R+Oj}pI~u+cvRV4{75gP|JoAwuAGN*eWv}Co?~aXQ>D=+Q8;U&SM&lMIDrCsWWdrZ| z?EP~$lGJc)H>D(mqJcsl-w9X8mi`-KMV|LMOE&-0h3tIYUlE(N#A}}ZBdX{6z1#mT z!K-pR_8|p9{7^p3S}K{Y4WaebV`DHcXSz*hH1dlwhUTtv=iL13BnXIUD7UCHHY)s8 z-FO0o$4MeD;-jZ?)_YQ8-cQe+fE~)oOtl`zt?h+pd5u6@j#N0#sOw{xI$;@aj2o6(=0G+%7Sd&6_7 zRnt|x%;*?-??b`bbI8?49l2Dqf$Ce`Q#7K_c5{C9IQAL^QB!|?PuKtkVkyhBF`wY) z`UF6NPYs^#HN@E87$e>vRE|a%^Fncff`;v3E}FU3o-7d%A`6XXvZl1LxwMMdfJ8Kk zF5P^GKm=R`Udg6deK2-_zfw)PB*BCx0tnnKnPNMv`gQbq=Ny$DIp34V0Zichxdeu< zZf}}yfAfGu2(Z=r`Cq&j4ti}twU=vW%uZk^CT!AnSqbOW!sa`ib!dT6MjBxfs*S0s zV}SGkLq-_Ay<`7^jMt+MMro^SI_7TpEgZWn0Y3aCT)>$&R$ z`d&I_k{4>fakV)Fv&&{TuX?A?C{TMaY&@~hvg$o54x199Dk6`2uN1i1z`*$<>lB{G zCYw3p_3%47nNui<&maN=fFC3LMu>nQADM%{8N@u$Wdb4quwV{1@wW$yZH?B{d#~0# zoSz?M9Nipx!QudjPJl!m6kBBRPF!AV*V-Z=>(PzeXfvP}Ey1geip&94)PO)5W{ZVd zgpQl%?s}x0)?YHW&hbu56C{@Ey4>4}ea| zcTo5o6o28(Wax?0S&5Atfd|_M4)u>aNepilgTi^6UVx6(_6A6$T}T)T)ppUdFxqtIfJMwDyRPI zRBr-!Jn3c@K70Rbn(!h3g<1~lrQ@pFKc~*>dbnBFZc7RlQi5eN5ZUOH7Nz^ z5fK1Gc0d3D5pvL}tTrG^g!GGyCJU%wZy~N;tA1iQ}$eYq?SmQsX zysx2Fc@F(G7AOAk4=TDx2(C^1XIg*mgQx@mA{~EF0F`%78%78@O0z;Mh=_}{aNM7V zm^cm##Qnz~c4~djW2RfGxV$*p}u^H|=Cc(92M|1!dn86VbvpSsW)ET>9x0o6R0wJ(<^i@P^ zvpOmy4oQv+4Ek1w{yM{Z&r9SqQqYJBf~D5l1IdE{Ve-6r_cRaIm)SW&{%fuCY3tl? z<6Cp`tupuYklrcmi>U}sfK&`_#$Cc2`1E$;G%v1 zPfhz^oND*KehW(8I06FtH06#n5J6RB(W+x5Zs$C3shIS=S#y^t4*JYNaXO&!?@Shxq!;V*S1j$`phDfwQL&=K#vVXBQ;Ep+nlzb}ri{Sv{TQDbBuE7R$x5 zZ>I!BOM}(TVCQ;Llg>d-yW+ToVb17a+|mm ze1n*t1!`>lbbUH++3f>_HK#sQzI)B*XOrgjwy6KeDOF4yw_V-r2c{(Mc3QV%@vrho z{iT{uJ#O+faEi)&yuH*8FHIeyo+bVX*Ugy;KAUdFJZ1tIK~& z=illOAOQeSUlrNKc@&@dCqvoosc}Fl^PoacMxXS$rUz!M1x)t33Q!PqY772VQ7DQZ z)@&LgA|;ov9$!_sJ#`5#ZeogH-_x@p6N9QuvHt%fCDq3P9`w86v!pkah`>W+d#}(8 zv8q=^+RfX1HQl(Ew7s-%YYOR3aYp8th>FTf>(02@2Y-1Y{;rsEA{U{4iwKA#nPzcu z_aN>eZl-+y(#a`z`N>zP9A!l3r{Cc}h3ec?)JIJse$Y%EdXmoIy`%#&B8UV;IEL3= z=&}u*ZKz9MPe%LWr5wo>9B56&#;y}PWbLw0A_PI%Bjc?>K+aws`R0Fbi3mvWwVbUE zZ$C3Ild`XSSL7!mR^IDn@DWnL7f*DKHtL;trunVg<+?A|y=#1@BkN{AkF|0pHV?G} z?2C$xjFlZMHi8g@2tp8uLJ))qLJ)xnL4+Vf-`@CK9!Jm7d~Z+f>qYDK^|@UiQ+Voj zObPRlgdjo?gg8Byk^da_pIXecYAB+G6j4PRHV=)i9mm8St&SU}z278|l1b|ExJ@t1 z$#s6eWCw-MZJy3HtMS)_AqYYcgdqq*5QHHJLJ;NqY{p-#&EK^x4fAus@*h`Q!hZw2 z>H8VX(llQX#-_dlcLT~|3(Sto-?F4Q{Ew807oY8%`_Sj`z#$FlG6@?~Ic! z@BNu~(dsk}iq`i2g?#_l?x6nHs6W;sP|DcR#&aVcs%p}I^?Tc59#&OK%TLEg#`*2@ zN=_ke>d68kA{uz3S!&)W1PBRQOB1V**q1!B+7VDh0T$|JBgv)Fie9^+uZhS(zTj8r z#4TQQ&q#=hYyne3eq*7{oK`y~&;SEXP~0Ufd=s+gmsG{4y*ADC!%NnY07WPNO>dD$ zhi8O@dI198w^cnYDEj}`{~o`?yKwlYf3u0gTjT#Uuy1U;z_7Xghz7cMr?e6g-=;Oq zzF+_W6kd}2h=B=A#7y7-md57$1?yn=*y{B>JFu!TjgM<>7+A6wX2?`-A#j5>|oKq3yQ3t6MRub*KO&F*VKe?E$Y9XjwnN$bVlN)G}C z7su4J&nJ8o4-j+S`)y()S`mxL-%JgEufxUTo<>&J^Z!{cIE^ecZd;&;Qu5zV)t~;y z-ctBh-6|G~W3q8e+Azk$atr(afB>5Dh=Bt6Okye1HuGJtYw7J-9*fBSct9j;^pKK2 zMt=*q*FtmpjJ!qWhkl37&mY@p&94!z=lQI`mqSXMHM#+H-cMrXL2k4AVunb_xmgZi zz(gFR_&zAFu1uCXHhoMO?IQ`|8?Sc~Lqlca;dkD#zyLw4QuY@U8nmlx>$LU;+!t-w z&l@z9mAP;5&R4&SUt7L?qXDa^rmuf`YVVo}5;gaJmFtjKMl;iSgt!2K0U~)p^T|Sq z%cDX!Kg>npQjj1XtC%zD)Cm#&vB&eb4Z+5u#Fl`aLuA-{y zzb5lha#m~Dtr8%{(Xe=wTkyVY5GFCLEumWNZ(wj+90vv6sHi+VN&%7#y5+3FYQD#rn+YghCt;EhDeTvexUS%4||2 zAVzTR*C=mVOg3%R9<}yf8B2cU9vJ6ksi!N@v+1{h2!Ri?!}tIR`wCNtZ;%n4rA7qh z{|gz$7?T{gC22VX1Q_}lm5cwC-6j7SEu=O=!xAlVggz;#x&P`TAPiE~qB0C)iqx$k4uyvzAO0B0>MEm{CCcqDZXWp;i)yyWpPhqcQRY&ucGRbS3G%=oi$4q*AsqZU^R_3Fbq#n6?ByxK}IKr(5?Laq|l}4HsAizWz7Bp=4hT}V@MH&@N zWM4xeYN5R+XoMC1zC-O9hAi5+eH{|dVj84C05xASJIxec;?)aeaOHHOJNd7y%*+6` zNW$gTle9}qcWZQUF&mloZgP^ESYpq({!jWW8og^i?VBf6-$KQ~Or|w0Ph^;5Lxkz4 z9R}nOAn<+{ASw?XE+nO+q#eNi@@*DjY1s8$OI!BOCzd>G_XMnq*iwp0>iPca6jRIY zGfLCgUB}AotuMK89uDPsJpn3I)xp_rG+fi*W=2 z5jiE9bNfLqVH(%%ezSw;mrwx$0&f?!>;M4_fB*{Zebk_TkHtI#52yFshK-g|0x=^G zLEwlHM?-{Ig)LftRYtaBO0oko#iNtwl`QV#C#CGis6`mOy?-}AFOQ7(t`71(e!67$ z%lAtruS13xuuRI0G=C*br-@Q=ffSLcQd&@JnlGmtx!7bbJ39 zuV#3-ZK-dpF;zyu0kmF>F*r{a4YcMW5c%e055mV$(111KP0TmB8j^Yh*g&q4Ifh(Z7f z_T<$CUboAz{r-1^@n_V~)iS5vIM?2^l%4OnF&2pYBm>=pvf--UMJzA-mFLev`Ua)T zQ-0p~oawOVt>vY7Q=~#_Q1N^O;sr_cUxUDEORqqx`l^Vq|Y71vin!E{&B*3S3N6$rDknv^QP| zT=ZA~2caVqpg`)--3f!DfLRiV*R$&yj(XOby>rWm#3;X=Ki$@%|owHq0_xF~wNg^xFYu&ZM`$Ygi15w78tc?Hw2W?D|5CM2pWz-M^ zL{z@rIVFecpERbbAAj5VZ{Cmd%dhID&o)myO>M zjzGuxvfJ8#EPUlO;&Ewl4b6;qG940v$H(LgV zUXPacIXB+h?ddb}<3CVu+YE(ypJKAJgLdZuVPE9*tql?H@fLf*&c5-9dLq6R5;)Yh zzLjf2A_P$9^UVZ6f_;k2qJKi?DFpZe07s#lZZdDL{eJ`iIic-&dLcR%r*iowfQ*1< z!zmk@*{!eo#KeZovgx{J-lOFCdtKE&O{PK%mfC;Ssa$4&&q7^AF>2h`M<^HwhlWyJ zC$`wu$5eWO52J|c(c)g19Bo_ho16}v} z@Tt%c5#s>>0kkx`d*<cYIZm`jjbR7eY(=UtOSJuZVbK&=hkLFZ&yjL}c%)I43 zZ;Dc4zH&VNcWdcJ>qO?k5dtc83WOmrCSy>^b$$oa=s7%qXNq~+1VlQ*Gjkbg*n|H^ z&HsAIXdeb2Wfhb~O&_K-{Lz&Oef~6e&yP&airejn+G5DNWX?@MKG&tIWSZ3OjM~AT zsi#&(P29#n<=_o>uv6no0XY2?%LzYij&;TRV-@67)@L@e4KDIbamH+lkU9bYfERDE zz=((tilevrI{kFj7LCCuIzYfx!*m(Bx&DYCMDbBetBpE{E?7+`M<*J8LB-&IC|BSS zJJ6{g8>n=u6P#A?lq{IxELW@hP@^!I#6ZKu()ReGaV`&9heE&i2f&!I1M$h@1UoV<&Qw2JC) z>ol}hJ5PhOOP5~btZlA|L;@loW<9kL@vf(w(iS6xs%ZVjE#mTo2n;^)j*Cw`nhm>& zl4`Qc`Q0oNvJ$l2@2ng?HIxt_MRB6)QeK5F%=(X^4ogTiY?%__^3-uqf|Qp44uA5|n;j<2Ku! zZhJ1^vhnl(rRF~M?)iTIU%3e|Aa0d5Fj>kNsD*5$M5%A4j3l{j4WunS3yre&U&p#- zVphAcTg?OjHxR0P+1no{j969R{!g}`JgQtxk6QFCTV^c$lV>y(TdK;9bka32v5*7-3N`aB{^GaH`Z8zuQ<;e`PKABZI}%wS5CviZ z5GkGFpY{crWGMTXLJh00$Frh#)bVbQWXaHp;m!g`)N)2Fz%e)XdjJ6whyaKXB6;H) zL~#wPw$aZCH|g7gxH`QX`ot}2QasFX8x48AK3DAmwxA!>YPOG^B`UpVDUkNpJu-x= zOhAt_c3qr?OSXCl!VwR=BmdL{Lu*uB;`vf4d!P!CR8!VeQ^u>NtVJ_G0gwPiD5@?~ z^WUA@J$32y+fDz}wZ7=^TUQaiYaG9AlGt!O9tlAQHYyv11OX`l>kv{%Io;(Fah4Xk z#tN~Mq9Qx3)Aq3O1+DLe=q@RV&fEe|3sX@eh}r&J-cxGPtu zc0|Sd|7=!uvSAh@@1k0Cu9(aQ=Dk#FmyIt`)b!u#50G{-e>v!I##gACm!DKjhgpXy z!E=9kZ0raEpS7F)`QWhLC8#)apbwqPr~)2Kb-t;x*j@4yV;xnqszPniXW61rhU|aW zI-3*9-(^&g3nU0oI3u{F05dq`I!bmHbGkKp65X}Gln=g)6>egv8xe<9QTQODrriUI z{XaEZ&pm|M>j2mC96!@xa()PkNcDWiz6wnO{PMzqk}%W7w^vL?S{MKTXQH^wDE-m| z2Vc#KG<-E>!d!!P@^X(49tZwOcMy4KTx3i$`3oIGmcrh$IXjK$-h%-UlzYX$N+QvB zUiP=DyxhLc>tFrbSIfobbHCqh@ofhyG%*rMgRD01+(=YO7y^-igQpYwxq%g*REGffzEr*|B<z_PG;T;vjEOX4UQV zYH=s15J1@L?08|?N6R8&&TwGKGa69L{uMhtb#3K7|HB8I00HQ8U_;r&(&yl&G-cXn z4*cs0&{7lXv(Sq0Hoy5|?&%U~@&;|oElqAT&uU`pEQotok)>^>;P@NfB+jF=W00Nc zwR_jS3!83(*PZi;p@(yqc!9!w!kXV@wpz)Da1ou%GLjT54W_1^KBu#2gg}S~e6g3! zC>gC;o4Cb~B)8c$VGbWM0GC1sfA+C?R_NZRNd+#Xg*!7_&;(G_kt^Ge9!uu`Uu^?4 zo$4+*gYRFW^R9ROW2~5onAv6`$MeF?)x#!FJ9!HB3e)^W`pJsweG8NwG`SH19PG_l z00YJoJml4lJ@#==>$kx0Ena1Jdv|0!myBHv!p!j)U>hfZ0fUq@_NXtBrjK#m{U87( z?{K;n(WQK{;vpPgeINF}c`W_g&BjglwEaC)1=S`m$4DitY*d~(SE7@roCLhZGC_Dsmen^w%@JcOQGcODI_<2&F@Ub zmeWM1YU{@s_?c$G06=K<8zLeif_H=dLv-&-u??JBq;Yv%C857z#??&Gs_P%FxXBu& z>#|eaYRwL-=C_8`Vpq=`XOE_KIbp{_m22nc zox?G5)Tfp>w`5~Vm20*~7ms@qyQP=QReRCRL;5Qq2*qU@wZq=VLc+u1r>U=t4MF3Y zS3KK%u0F{QxwtvhhuhA3|27dh9Q%=bBG9Fe+*LWjP+>LHziOLr*v8N=c>Po~<~^qC zHdJk1vWMA8M?AsIaK&+eM03Z~ur;CZN}2!wWp`kQvHbsdU~MCNpH{e+X=O4@XMs9) z6AaX|x$?sy?)LiK>;fu3t_zF$mb}DpEV)Pv!mU^Z^0`VXdy^aIW3D zupRdGi+*mZDdqqq%F%dHowaM;e5@bNd6nXCyR4Lc?zCgE`L{i;Ts6*3_Nn!EFX3JM z$e$98Z%q-H{s06*hTc6Q#prjMZM_X58S%UPU>QF*61>@sj7oeYOr(L8nu$l0#CD}l zH=O4-RO|X>@Bj#Hapj_ai_~yF;dYfW&tD%qgpb~4_IJ0K$%et0tHjm22254Zt5~qj zuz5n)0s+CEW~aB%*LDo=Pmh&()6IHE%qE-G*PDWZhp99kU@MfDKMu{2*sW?8qgUrx z*>=+$AMuhh7X&4v*-TMNl?~S2g~N{@y=m*e@EK@2uFJV{09qeVwloen)PKk51~(CcZ%{&2d?XQ> z^+aPEd>+TEjmzJYuybSRr}vrKE@%J@{VubwN$FdGlSbt-D`#u;jvQ19E>W+27oTVMY>)O)GH2x}=FxFM30;*;rcExU zP9L_(76AY9cI7C2(3a}t+^G}AWhdoHQFz2UD_||&+I@fnY^G6_< z@=RJft;egCS3HI~g3loDEc1$6mAt+H0D869u(M{RQTgQkyJjqX!vWQ>0TI(ih=>6) zYIXA}{qoqn#5i!FcXFE(zNr67{K~}{rlAGZIZIZNgK zOTM-H-coaA~ zQyZWG58YL3Y)|KkQPS-BXFf!ee598vEFaO6_%D#HQD~hR%I>qT9p8K0)Uc&Or89S= zYL!^LdkMU@X763*0_$giU9+K~_*-M^*C2RZEA#wCzCNA->VV8Lx+Z{mryBhk{-RFv z;k4T_>2#{yw2yZxi$7UPd2b~Y+nFL_i<$MNUFr~$lxAp-Wc_)G*^4PN)ierYc|Wul zX9}Gmrz7E@*S+`jKgi6D;vuRZ!WSYxjc9YWu>5a1KSyKhR+QnLo&W)omYbfpkDq0r zeb}Mv=X&{VDqfC`ny`11{bt$!aD4fQ-Hs%O6N5I}TNYxisi_#XZPa`I+1~B*IiTWo5vB8H{4XuL z9X_j~hc5LeO#S-k$;9zCTN=luk-vI)Vg1h7C$-Y0ZK*VFCz@A_t%$ca+VW zCtqUHoBfR2;gr+=ObU4IR`sV|{f$3>Zh+5)*f0PM^12_qY_4u+GLm{G=g(DldVQXC z$L2dOC4u2|WVtjH8OJZ*eI0t+J5jlY1INDgFe=^#J1Op89_h=op*!?cwl+ zl2zSg8HKS@55+3 zt?&9SFi`|RhZH)!lef}pii7`uH@u*{lByhsQC8{iex+Uh?@OBYjIPP8Xr(aGkUG5d zLXVy=26_t>B$NyyQ)S$B5?`WN4-v=^2z>+_3J;G4E$2$@G}4&7@-4lyp{6PYENYYv zYb~gzAP_wkwQOEKAe8!cc@$cs7fg!9UXG+y*MQsL;c3yDxSch{X>_Do#QW~}6<)lAiP{hI z5tgTZ-CaO1t-JlZGVn`}Mm4OpZp+;7YyLXAheQbx)aB-w+Lfi>W>^zkGvxc-di##9 zo2c8pGH1&dcY28S!OPkxL4oQ1l|3%Ssdagn#^q-wzVB|=>j=bXx{fB4Eq_pZcn+1` z6^u)4pXa~DaRsqj44y-FG7Fii?Y9sZCj}_U5aci7;V=hE$7jKJV{9`I~3?39ae6s!HvYIB?>f{S% z91sWqMI^)fX-@)Rya}B-Nc}uv??t}FBc}gf4KYFcx}&e-LG@uw{O;GG{@pmIMwpuh zJ1mj_5e7G}hWZQv!;`yPGa|6Hu1Zv7hk)E!GTeHbu4^uIUze{N6N6H8!-2QO3^e-R zb^0DHmyw>q2Q?#InGy*AGME)ldB}2H!6;|NdfsaCK(|o@P5jCqYC|dnw@{E85a3| z8#8mqNPpWK*hGvA{dy%TcZ@PjDH46I$NRT^!#nD2He>9~X_qTAZ|M1n za@?THPN3n{pVL@t>uLb@%-WrfR~GXaQ#UNM20~Ew9;fab|4XvwZ}^+-MB6FhmuZAW z-Ol58x~T6y1fD+6F@3yiy^>i=NmTUKa-HZetJ{{QgIc4oOc>Y)Y}2n%*&_1X7WE%I z`y7|&2t4Vq(Tkj8z&DU;;=>#O5dtq>KR;FZv6@Uq3pY=PH=P>Vit6&m zbtTb7*I^>)@Zv(u^$2|GGsWV>d2epOs{HNhGN6IP_E12CS*aY1;SeDn;4B8+=dd2F zeBbv_{nCj!dPR!Qs=lf9oKRw|(fKcApf|RgUTpXPG^edz?^N47X%Ib?JomdY(0n3t zR)6a7>)Xuggo@(626}QC|Lxg%x2RNn7`-~-!`n6{J_u41&NWu*|9zK}Q7<{SeYk?r z@my9<#^q2v@!B)Z^^i>&4o&xEr&_m`uaFxH0rUll=~N3}s224wfgAP6sEU~7?3h+y5$3Aly6L94pu5J7 za8AcVa4lyJ`77OLRM#U>T!yb{uGLlRnPxL^y#~vJyNsSB*dZ+#Mm?9zsO<;iot(Gg^wjSF=(pUgofCjGja?_Ei_fD6wao>UM z-Q#g=AZhI;yM3=O)p5~{vkMQVp_+pOUSMXsY4maH&^g$1P;alwDsLWOyr+N2cS`h< zRTIdh&M*eKXy5@9FPGT}XE@!?+rDj{>TW+xX4i@Wqz3aX4hD7Vj~#K>TBkeC zG98BtJ+G0F{Xn8|A-iAV7n(~9eShC}J%LF2k)yN3-AjMq02MKPl-g!!2|I2xHt-*I z#tg!`{godh^RQ>#geCtMC&JVF_^(mghX+Tm%n!=BS2cl?^ofzooFSt7m7#%TWtSv*#0$lTTPD*b$%m(J(bI0KR&l6+AVgl8Jzw=j-do|X*?>ev zeM70zvgu}?%wB%}kq3*n^5p_o3bQtS z(({oK6Yho~<87OiO;qpex=WWmG=AMvqUL`;4W}s!EHvrc1@+V#7Mwm7zq@V#0S8-Y zyT4(3GAAqPj-;o}Yl%jf(NkA>M>5UUs$lil`VGmqkGJh+K7tf=KA#hQy?$!Zut|7% zFV1Is=qT^KD{w33l8b2q;nR0*<2Tn%lcu*N{QDdVew{^^jY~4keMub81GVoyI_H|$ zDD>x>G!pmSrC<8aSE+w|+AC=o8aYUiUUg2Z{Ig$bp+onUGbM9nO`~E(`ZR zDIkHE|@xx^Kqr{H%@j2DMk z%pod8B*=IbC+2k^5n_fD#4hSBA9ySmE&Omib#go~w}ax;8kzhr0je+p68VEgyf6)E zfft2-KWy_(Ef_xuc8eWK7Jau^^|k!zpTDc9mPlO>8K1e&R@s5AiOD)~WSYL{M#b$# zr1$Lt=H6nxBATy$kxvg+dv1C(JoMFrHeKWtdKxBAa0Els4Hu2%WFE{?|Hg{BO4rIy z!hvC>T4YYEzc$94v00cA_qfyG$d>Pe$i`@zl zZh!rWVP0=M9__esAPGSt$Viebiz3LfEQ=z@vMh@rs)8U|4hYLoC{m@UDk`aJ3W=1Y zB$-M=NtC1{nL-eO5)w=W3Q#CgfkKoD6rfP0Kq{u~+im%~|7G0#)(`FTv%fH+)f|AvM)=P?&I1aHmV-Rv_;-aG8arp@5ZjK;BPaJ3x| z+rAU0O;mU?s}KCaz}mkyX3#0V4)pRTOHc2ek%0VUx0&j&L1##-%GPF{{1`vQ+AnWw z3QKIrtkJ|m=HN{EwpZP}4ZaRMTQbsy>*2+rqLu;v$n5U+nPHizOmu8FKW+3Q%s%rz zd9i9ns^9dajer1qUEm5?2uOarcJmw4;n;BUm7V&Gc3ZAeELL8I|F=_EtMXK$RC+c* zh=>9d%%0beZdUN*6DjZ_Akc*YIBWs{07L-?J^7=aQ_rqS-}UKpGgtBAs?h=|=AD&Yu93si4FD4uo6(?uMDqP-bV?Zf zv^C-L{MKwW_I@+SHBln;zxhFCey>qS+IT8GN4kcAsGZS6;5KnO|yEDB%G@5w$ZK#NW z6baQLA|Wz=AJ^a(BgN&4}X=K)Wps%w(_dLBRkX7;HY{+|+B9hWSLnhfe)l_!r!DJkvd>0;tDN^@C5 z84IQ9aV$KY*1r9_GOn(h1IcWeMvc^DOoj7N`!X+sTWY8qo!Ydc=% z^@nZ-L4^h9ogksl^d$%9ujK2OOYUm+GGDO!mCB3IfCz|QwL73D1EJi}zJ=x1EVJUz zsxS57Ey_>wU3;0N%b@dOR&gU0QKGd<*W^GEXzJ^K=C`y%R?H@i2X8+wr(}(o*+|d8 z@cfx{LWD*Jl+@m$VXc|lbVxoM#&pf8U=D+p)Z5vx&?gSVB z018TmbY=*}@R&Vyo!e@!tfQTK^;UAoQ$ASM^cHGXUVm;AfybmPH%+;Ucy21Fv zwef8S)6r#&>-dO&(k0eSUd3;>I%C!R56mzG0C@x}fe;1(c1!{xNG3i5>|u3DB1d?d zK#3oFh55ML8YIDP_??d};`QHI5CDKA3^Ke51AN@xdcFsmn#w@He}lB64FA_R5I4eU-~z_V*I_+jjy-6ZX0H<+9RMBXS#{ z)VS?Vl3Rk@#}0A7&qpR^fg4zyV5iT2orv^f%Y;LIcY& z2hXW7e$D61*E|%YmS<;%g`uZDV9!+8EQvOayhPXYVpjS;G=6k#{V+ zzP`3;AEI)($265HYdDcp=uyus-`BZK)0*^{QQBZYJashxgUiN0YXnX^`;u#8`cLm? zTed~6NJhh7)AQ+Dd%mQ;M2ha;U9zb9RpMzr)eU-wPdwhC+z}e>IhS4f(_UjEH4}!u zWHPv|-{Hgiw(lptByqAq=jFEC$7{>_GqLq`2q7Tt*usD z^yR%9hy)0WO&cvE-m*cf>9(bYtgwKTlhH!zA;D1^KYtKE19-2J#}d)=iMA_~$A@eZ zs@LDDM-suCp4Wf%P!zky-}$};cfYs4y;uzNo^?C|K`1%}5+j}LHhUds`B=(SDnKAg zDh9%LztI2@5CjsyyJY&Hi5eRSk4~pB>A$3-vYE}aFHUQ;0}&G*9^c)x?c-RZYPeq} zbc?>~Dlk+p@Q$T} zyCB7XP^H!lWmf62!p5B}g!bhRhe?d85o<*RxarthhCO4VS4U$}*L6-#JZ=w9a|XB} zXi!XfIp`BiJ%jHagOA>}+V}-R8iNs!rrjN@Wn&MBk*HC=u;5l3l`%s3pPy5N9lmz^ zUhcJ=%q!*OdeNLP2QfC*nP_B^@F;mAp3gtop07)Jo06h&Q_Feui?@PPn z_nrR>PdiP-`8|)T>-V;`|NFfAyvEQwG>ak>iAFFfB0>aZ0E}rOl!YBrvFMKz!_VE| zh0LTJwM_9j-3DuLG!+J!j(Kgh%@q@V2&>P zzLalA9s1WsqEo3wv7a^gXxlu@s$yT{Yd5pbH%w^!pFQKXy{dK*2=CQoTiIUOGkmD+ zVYdZBF%EtOD0|QvVb$Fubf8-EHU*+m&ewLOjq<8`U{A^5kudZ&*%#ONb@w9x5atjO zpn;LV!x#+>>q)KQ0&|hu>mWZaLni^o!hsmZ?YK)~0o-&J-#(*Y#AG!)n#=Z!{M0i+ zeN7^=#T#lfP5}Zg@<5THjxyj*DLAEP39e9HNTXGpI=}zEr-*Hgzv6EE_1G8ecIss@ z&ZAms&FFoeIi@>Ei9Kpm!dgIZA^;Et=RU?y5aG%pKkbsPdnwF@0uCM zL+*LZPmNRD?K$gU+IY87CM|5vVM4|_zMS1v^`N_U{Z*?QN`(BAbT&~e&JN?OUPT_X zv@v|d;^upH5s;Lv1aEI;<+=zO#`_+HYd2tcD-sKixwHfTAPSbt(IIssF`Y2O3Ey$QZI_tWkAkRom_-;KW1;|(Ef7_(sZAm_9y=<6?qH&G;FU1 z+N+)YA6AOU0#tYQWxMheIP`tgM5aG-;x02^;q&?Fg#<)8n7CK9UuO)0{Bk9~)-Ib5 zA3OFl#0U_wkCl0bzj+S;Qpma6lo}0-_T9I!^{Q9uNDaK1IU;e`#g0-lpVm|-@urzg z>bst6q^P_A5pFQ6UH2Y>KrxE^%QN4wAmld>D*t-^(qz~m00dd5*=xiW-LIOH@5Jgi zsL6)5KBgTC#C~q5;r~bLTxw~PG(89Dhhk!6C^g65ZQ+utKIj`Vf`n(s=JM+eh(>vK zBWQ97fB*~t*$^)K^0<0(ZVZxgfpOA6pX5f_`b_rDC> z@(5232In@M72o6e_r<*bFo=vlAMkU27k#?OjlcH3<=F8v-8NB7^tru;1x`qB0}$QT(^?>?$zpI$8j^O4A)y_vbA?I_Ieyqy9>X9P3?F1`DRt2!NNq zzBqsYKX!lHVWjBW@=S|gZlpz8-#Nc^WB8iurTyn-yZzX#Fqe<`%?z8jRv*g&&4w<- z5CH`-pa23VP{;rSZ(HB2)0i_7w(ZXPt}jTe5fKm|5PX|tLV2&*IGl8Fm3_NF{np6i ztn*j{g?FN05_{Tb*-SP)K6~^0tmmd-cN0e&<&d6tU<3$ib3e-s{-o!nJ1Y0K#Lw0M zh>GfW*&+0`ioNzDx?jr1?YExFI8XUmQO|8Xx@%orsbfI-z`rkq_>FG$I28Z@fn_2u z@vpf(>qbXAJ+xaDJ|mLMc8fW0+ERPS-H5I7zh!IO-O?6@`1b=T=ER&W%fbr7+5T@mV z1eZMpdolt8gpV_)qHT2GGg)R6+On$*fB^sr2qdPaF`h!3KF5Uqo!uF_r-s^VS9f+E z#yj+`Qi?y3?C<)swRmT%$6sH*e^eT)Aq|X7v-4TUY_u@8dR?8tF`^HVzK#NFi|3lJ>aXuQ4Wma(G7 zh)>|SsGoB|*SfjE=rkm&RB4Gp>bcmzU{tB`B{=v5V85WD#{`6zNV z7#r5?=Bdn6-}JVB6-gsq{J;J{qwz&_H57cB^j{1*Ro{(M91tKxT!Ru|zmt`~h=>em zG6O&^-=MJ_@XGzo;MMVwR@U1!Z18UgIbELQcLIf6yDVNjLi*Ek&wrQZ1lqQe(^tYm zIR8_l@qyUixDFQenz;Nhr4WiFh5;x=2uUQ8NdQ3tiYkJos4AfeAJQR?6cGdw1Q7}d zg#7b^ypTWcB z0sss<9|AP0q^_FgYL0(@%x#JC<3%OF4pd1Vjk7a81Y89+Tb* z&l~^;Sy84*CE%}ZKKd6qHpTtuT+ANIz=pwoKiIybVJ+DwbHqS#T4~(8ME;_+6sr~X z$Qa#7dS^cKi)CtlHDkdM5C!V{B)ZVh9(OwTLcjY^(9g5Fj+#rL)!i?TY$*1H=9fo4yAwv*Ud{eUD_5 zGs<2P8TA!@0Hfh?_f+sxtK5kcZdzHYOxh;pvwhxPx8*VFEkE}DGaiZcwhD66Fo*&! zfovK5Qaj+u{krGAfB?a{N@s6V+;3>zZ6sK^fQVrCQrPAGb^Z+9w3PI%th9CNFA@%i z#kGWZjL&T|R1Vby053jQQ8AFSOKYNn3Zvdc2p4OGo6IGbsHjLlh$k?BA|etQZ!^LV zPn^$^eEte+plvHu>76#DIz6Pffp4+DnwtEvWO+4_(+Ns(ROP>@Hm8Pv9`8!SlakkW%Y^!a(>D8$_^di_Ak-COwdvv03_sMMnvJ5} zydNQK5Z@7vXr&Dy(sg62sxeONoYu0>qcyG7$vzEbHZ&jrh!9dVwDIqt^?(3?Q=dWF zAaLO3BMok0rhNC5vqUM@bG5y6kVFR4Updlz4wy0nDYOv}Q*Z!^Va}m|h=CCJd)$(~ zs2}uI;yWk8=kjLCb9~O0E8SXsvQmBVw^yx2ja1_B|Q@zE}Bcehv+{}!zLq4IA-S=3jcQ>o@I zvq?#*t7!}bpE9)p_1PiYC^YqGUEtZ@TkjV7ejfsKy++2LOK_JQm)qY!r`k-zS{zD7 za>kzFPOEil)FxX}*Z!BV^X|3m_UTjTSJ3_5aS;#-VM4r!5fyzGKP@hwF{^ti>*^JB zBk+J|%RW|Eh=>Eg!&zN!`I9UM@9u z4=&#JaLr0Sv@YVn`2uv*ML4cQds`~0dYn{_PCK1`e}d=27J>%{%Ls?_v|r&tMV)7^ zOI&G=*GWFlH#ojU_J0kJj-?~TS+JOKrvkP9YwCS{TJ54qSmj|4T z#%NJpb;&qJ+k3bbg6xi{p6bV`zVO^aeZxTQ@q3`s{BLVZi3>0)7U7J zGMQ|6oAEqM;n2sKTZfw3G#&HX@x2j~FDCn0`j-Wy%Z4PRg}`5zeaDEih7gEihh)^? zn43GF-c}d=ok#!x5!7p&`zM2W^fxR$l#dLSTbPf*+so)d&RkUie}r0lzI}NhuA7Y= zEfOuLJ~}`^fL5$R)!8HJ7``=M9oX0We#ApUL_mYJm%4M*bchiBTG-CvF}Ccjwlk16 zOUW``{rx?6n~U%jP-szVy$c-5pI$oDk;*54 z0Do5z-oRU{tec7mrSJd)xlO_B;s8JkES+fp0=-ICL?juVb|L`1>38U7|MvGn_0+_v zBz4+ONFZE>wo2-hUKtS((s%??7%X=12mut6-N~lX?)5)gTXi@*54W+^R`DZDsi;ka zA|gM9zH^{NNwhOxO-0#Wxz5i~yxSmye)r6<2!;cXZsHbT(XIyc(V&-kWQz=bcxxSi z;k`%z5F!Wrt4RQCh=>s?2h?N~e02GlAcxN6r)Mi4G&b^8xtv&yUhlG5M$M%rZViy1#-f z9Jaoyg*0`J>qW~&01zQSYr{s=mT4jo)NHw>+cQ*3EWU#_ePjAH5;>w*<~wU6x_b8< znsrg=9II0JK&)K}U%v4G&IPnL*+Q%~nFTp8&io*-kf4h)j{>#_6^5rl=VvG@*}seW zlj<-V)7MibUAryn93D5JdZBaSqdo%F%FQtTSpvk7J_*XAH{fIdK!h82C(~P9jh%XR zEh0`*gZowjKRW&#ymScs6dkAcTpybF&M>Y|r@v&nuim4GL_{DQk^u*#T+p0naZ329 zOt<)>t@Jb!ba_aLX{~0P_UK4z+3r%uA&?8#mG^z^tNnUiQ!<91rBJm)xP|oH^;mr0ATU)CiqM*% z?B_Abc-E|N@lrqSz5)Z4T4Sr~<{GCMmR*&m#MHis*Rws*H(mX1rI*PoTUH~Ul2XyL zQt?0lfjMe?(%#6g-VztMl{8F`eca$Qm<}(q=0BCTzj9$nLJ%PcLKzHxCyLs!drv{_ za+<7?5=k$Yu7}6C@=li{yX*Aqb&6B#{ASV9>|1s4zL10^aPqe$9EScIkIoWw)A|iOxoOv_1a>jBS$LqfT3r0i;1FX+c zM$(N>(amu41tuAt+uKVLP4JZqz)N3EUPZI>OXvx|1`dtGCdY^;_qaSFL2*!}6^ zXVb#g7hgudT;TLx9x;)e9wEzIX`Fr&Ed%y%h zi*lugp99bYMCvW%3H6<3N}SwCv{^zD{dek#)eWsiT3I7$Jd2}rs_Fh&WddsWPt&*F zG;cVm)ih2^<)?aumU-LWBGFVrdZ(;p;HwTj``amyt~oo^ftEaP1nlU|XqOC<>NyXG zbEZZIL+dT2n{ZiGGO>s09irCMto|KO;i0emcwaENx%amFZovX&?x~ARr;>o&t&9iomn0vS_P%^AF< zYG6TALNdSMKJ&4=pk&QGhWV3PD?fwAc7i`LYI9GZ`D60&vOeB>$$k5s3mi=z@(tt= zDj&+tdm;9Zzi$`V>GHE+fC017F-5AZMx`_VzHNVTlkDHCYhjtC<;?djXC*t&Qew&z zcclP;3-6jp1iP7ImwkMOZ=%3pic&;&-tRXF$Ef-){hj0xZy*2__cST^ZJU%yth+W8 z5xbn?aQT008enJP?XGwIhF?ek3mGj|D_&TLfD=0fFJdA-2P;KzCFWfxw(IFtj+-<; z7r&;R(xFV|DQSY0ioD)QOJFGyPCNK8U8|=1On`T@z=FoDsE%XOby+z`8WmgF>n=|}yYUT3 zM&@k3mw)S?8W*d#ez&S3T3X@kVm)3buIM5S-zoBc5370odqhL~cU<1*Ant^F8zQ_f z1L<|8`0l*b`xHVV+qTOL71OW`was_YTJ?;lyd38{H7mT@yJ6klUvP02e!hLwZKp-f z@p-*3H{x+30E!<1qL&TtlYcaMoTh)qW5)Jp8$Ej|eWM*9*&OoPXxx|DvQ6gBUFtP{ zoVGeV37x%kB5afrL=8AgH|#^W>OSt;BNcIzr3CnvoU26FCq+3}89 z40nRS0yqK$PA&9KR0{f@p0a5G^p{^aXcOuyP`~=K$dDy;NcO(NH`SeQCkH-%0m5yO zl{**mbz1eyVZbu4v`fr4DrMrehQ3F}n|y7)hTJ-%;XKi`rSTp&KJrcQyj;{PHM#0{ zQL?>Z*(m@+0KhgzG(Q(O6oS0{{LgXMC;I{E1zvG&8*ucrLRCprAy^>88-*$ zEv;EY9ZyD_2%Dj1c)f1@%I&scsiRZ~i>y9h&G!i$00g2f&9%Sebrrl*Udt#QT;>BW z3-Gm+#U%aGLD)g;t325|IvnzU+1U*)mxH#)@|t8D}iNDO(v5J4WH-lS)GN{q~+-hWF&-O9AJcD0{x1 zd_)i~;#}Ci{7F3e73gg7Ae(6qAga=&KL3~nPcnaI89F}R7~`+l%2S*a+HV#L!=~#J z50;Edh;wT{>&qV@^z#hY7i03j?m7K)5t$O`-~cm4grQ?Lw)^dY&431!zyLbod!k&77Y2KEZgyyNpBoz+Xx;xRBiBM@|z>OM6S*1aDM zdjh`W2@f5=c;?m8eENVuh?o4!Qq>-3e+d9YMeW0V<6Yxf4`%su0G})2)c4(5@t_|ls+T*6 z#0XOI8?!o`?q-%BK{-QQG&0v$Fuv3mC$BEMzoXFdHe4#-_KoU&Fqr5f@?$S6S5qC4 z^jKZwyqS~guc+)AwdXXg7uN&6&J52I&1S*_3zDsaccyftrE0fca_shiA|N?eQVkdT z6^iL@pDxSu?lP3s{Yj`e))?y9J)SR}p#xx&b;RuZ0Z2-uDa0e3 z`_^VikAEs>0mr>3p>jOuQq_G0-)H~_vE6f}m72)Z+ylI4?9GBmy1sJ2z}L+xeEhxP zIk=oZxK9ty%rUFlKFUr4fqy1K3X`1;ecJuM@Psd@8>yfwmf1EN4orc_ja!u&RyQU`rfJ8LE zWM!UmAR>F9D2hMGvk69pDA1)E6r)0vXi|tG0YZ?FkdTm-`du&S=sFVfR%>Ja)Ui+y zBu4h25Z!u2o#&Zzli4(Vk8PR4Vf^3eV_w^?{U5!|U61m%~3`+5DL-o+($@iJ$T*t@`pfsJhGJx%UR zit+~DWcqZ}+gEP2tGmnXY2%p9ZP5^O0MIdy%Hd?`YP)xd^ktM)^86z!Z5*2Qn3-xd zEfR!&eIhVLJAGd6a|x_1M(1dkHxCq+3MbHWPmb1_KHhzq{pjaneF>ccdKc-39Ir^Wd_r~kYjZZ+UD#6q=xZ47%f0aaF7Fm> zjGFwe0VM46AU=|)9?J)WT^fF`eX1T)#AOqpJm$AfEhcyT%>wD$jX zTn7>vB}y)1R&sysVzbM?(7k2n;B2N^IvX~ZF`iIteV482f0wV;)0_50A#>VC|?_ezhl-7)q+5AD(#`F9*vG%`W!jc08p zA+O$k2P5Bgml6^(3>nw%r;{bMMN6D|=0)x!kS1?EC)qM~Ldn;0^wb#PoS<`~AWuLF zy!F%!h?f; z%pXguVM53XsDf?F+k6+|6m(_cU=#ETIFuD>^@)t-t}_y_(nlSHRBkb)eZXU!_Fd)@OP+u z7A9lUi@(OH_-m^p(fl!x`HiZv9>D>|13-HfgX1PH8WwjmA?gEv929Jx0i^s01|9?!77{n2lnxsYGxiWd$W z>td(Tzz_g{ElsB9ai*$~>kQhgN3!awP)qqX3u}Q%RWLM5;~G5C13fz<8%`1&I*IMg z8)5U7m9dL&jtBri6NCZ`iQXSIhf2K%OPV+Qlm^h;c}i$2a+TfNOmv0bp}vijwQBW% z^>=RIv))V>k_|vr;d}oq0DXWkbM?H5-9RGevVOZU_j_~aRr$0W6S^y~arMI6uUVR8 zmqZqsAHW0GTtoqWUyt3_hlHz@Ox1S(45aqd`i5V_4+YHiQzHiR6Tr)$JhGj>jUnrN z8KWT_yo=IH{U!7UH07R+p|QG%%#a{NM3`z*u)T8Gb=QYT5dZ;kfah0p=j%is`Axn% zmbdWC@vd@65fK1FwkboU6h4@G3&iwGv zogXrN^?mHr`ing`Ehcl`^rJ&-pMwAhl{c;2C0o7N`FC2lD^Y^2oJ@iMQ2$`7UUm~Z zLj!N;5|51%te^P)2&3;#RZhrmc`+@)Pm{PpeRRf%1VLBy_oHupcGz>>uplw4Kaz$v zU-0Q#z)NN!y7-=*15EZLJCr%?!#>8YB!AmO*W(!}F7`)?X_~Xl4NOC&6|DIpel@d; zls-@Z6ptB0cO6-7UI`G`oAYZ)j>wdrjS=C=E2MqVr>cUh&B!uEW8vrw~KldA-{ z0EsQf)y>Z!PU(6ncKt4Y`f`?7_q^#{@jL*C2utst*M+XcjI=l`fCHHh8IAfEzgy7k zKlg`iwBN)tpP?qxs@go>JJ5*<^I4N*%Ax=w7!8YZxOY775we%N5;hO+N zUPufiIdlV=J0|$T9uC3Q``H!=M6>Dd4$7;qR^hY&5FuwnMMaG&+O9eCDYtH(C=f(I z0m-R=@zFU#QG4$f`!|(z#0j+e)59jzrPng)laV(kZ}LS{=BdrKfB>~Log$IpM*l+K z;r~C2y;RpeNbI+wBxv+Zo24Ha9M!8Kq`l1#$pt1DLIza-fB`N`y7UZv*Lfi4;3=yz z>#fJ`RUM8x-q{ABF9M|Be>MREc}DY6J8v-@ns34-vhw>jr{J}a2oVZ2-1^4dn<*7E z*_nH_QX(Q_)4m_&Lo&(FAI%vz_V(u=>DIvzcbKZ$WLWK%2!RCQ;b=rl%=AwP5fGl= z+TwA$71G?5H)ty)<;n&q6Ve0$Ab>!CX?#J-O5s%z5ke|NQC2MIv+jKi>+Bv|VIs2= z>eByBK0WUA01*c7^DIBNC;$gNsT$2eS|SNjC0jnq$IB8^*8Touqb6b+i2Ib-CV>!= zTm%S+h^xdkA-dm4*2-a`5dsDL@fL3)IqVS;5B+@w4e?Q=g;MBj*V&_7cnhBOw!aLt^4p)p%puyl zN7mdjud(iK?Xvtx&_IA8cbC#-L)EoosCtm+GT1$N>ojhe^G1=RLxpt*cnEjL{ubApNHti|9U z|Ed(AP+etu+WqMO0Jd7!E#O}{n`h0>bW!<}jhO={ibN!v*zmJkW+c61apYfn=ewMJ zPvZMzff6ua^~G*7zTOv^-inon03u+TtvkBxd=~H#CgZid z$j8Lis{}yEjGIaRple=UVLS;YJ;=^%-`h_$JdR(i^#Q71=!rWcmstt9tk3dR#QG#poGkF{Iz944&Y14d~-+V?XHsMAuLLi6968$1p@hLUze&-xHlc;EjBZ z`{7#Y9p7SMgw+wx&6BG#WeNB)mao=MJC;rPf0Y+wwswO(ZCYY)9C_uOsu#~~gz=G^g3nV9R&|HF4h0;~}NFOxtdi*FOR*SRa= zBt@bm!1^0@!_QpWH%pmux%9o}Tdl>_+>aDPsh@RIuL3#N6F+NQ9^3;y%??J@(mVbYFvG{r% zzx}|z@TG1emA~}3lnwvb{C8a4dLPfpHvn)k-Xx@R}dzOuY$pibpR^0BE>e|esBiw-MvImS+YdA*Ja zgfqWUR6{r>)yAY52#|qG{`!Cb)p;x^(Xk>si_!?P`srC%Raljt9TJXdQ5S`4==3a6 zwUAQrK<8S=u@*Oxe_F2JJc+xfPMbpBQs}(r;D~_~oxBa{$MkrO#t!Kh$y&}@%nPK^pa^= z_TA2IKYEWdz0GO1Uf<(A3(NHPsq+E+)Oyc;8$7$OIsgO+hi6prAl7$TskycilKTV8 z?bn(jZ@hlh%;xY{?`CH7`lm=gpa2bbFVdA+yp zcbx3cOKCqJS6Ics6or=yA^et67`yrg$n5-3kIL6luwMLSv;QsT{bHNnzjux0B6PAS za=Kb*9e6@Tq3Fl)?D`rXES*(D*UmBrrky27if=O9Z=M<2P(M(8JC>LZR@dE$@$(w9 z|1a9zCyzE;sbb+3u6gXr&f7t%d?nTyQ*5>N}{85}=D3z6IK$>CC2@>>i zslwmCZS}>Ho4$_D%Z-J=AP9?jP2NI72-82WAF>FF5=%Df`}#Wiyt7!X{(p=4#%yVw zasQubs(p_Nx^&i~wzRs!p0lwx2ONcH>&}`XVKVLaWY&bu18S`8b_IO8p6DXrG_4)Z z4>ZqWm@#={694p0_U6wn_Kd1whAkWP8(k!lg2Vqbfv9}*wCL#2gdO5)wdY2T!(^jn z!b>?g;@`*6IIR;gDMfG|hGTD6yNH?s`!B%l5wV{4uJ^d<(DSF=Yc?4KT@8B_on$Aj zEn>^XxO0`CXU#`Uw^TQ1aF#4rTl3s0&ZcJiXEdM1pYV0%Bh-z)9f+_t<2AdIeuqLl z#!f=>F`DK}^sFIOV0K?a@GY@d_y1m11#r%K5ITA!Dt0XMM$!t^!6y~q*3zS$y{}89 zv*GBX6yM%g&e8j5uNLXi5nSXodI!Y4&!A#@%UWP&9@QBw9ipA&7coEW7)T8u{>da%VSrMO)UOOW7Mg)XLBM zY_8N1m^A9!FQ{{*%kC zE;Swv#@$E$OMZ|e>3xjjBbB}PpSOufcgdoxw0^XOqUcxSa~Cpo-mE@0mr-TUT8-n% zt$!$nAhqHKw#ILT8WjNocHBgEqH z(X#IT-Uhg_{IpL7X0G)}ZGOPU94Ux}u1lP2%iuifX`h6K*^JCMWLP>rk9w*Uc| zWTlmb)az6BKmchh{s65zE@pT4<&*~>L%M;zVT{0l1OYOA7du*X!`hq+K}4o#f4|Gz zn-3L<5CDKBwngJa9`t@pR?b;M(hKM1xSM0GK2JvsaJ8wJQLV5PpAGn_0k4jk#>;Bf zV0e?L8p=o%^=$-8Gxk|yk;bP;MJrL)rjJs$m+I0`APii=xG60984&omcl+A0=O&xIed(W$3+K&{S=vJh2>0#_06Eo(o@mW#qRmrqZ9| zQ}7Tyhw~VGT~}Vw;t1d()I1!U&dOV8IJ8%(|KzC7R`o-lYKVTl-7`O_G$E_&n`B`U z3)-_y2v-tTfCWeZeXdY
    o44~ajrbf`f91J$Q@m;nGfSKM%AnE(Nere^idE2-Fv za=ai!1{;FPu7|a5g^68#t@K_OSKh#D{10>7Xt|x^TE+xO)BWs!mac6S7E<-EWEHM@Ko->tMVM$PsyZ}{(Go(N9X z$f=`_#7`5-mNUKRiQfObk%LCBqO=@Y&=${OC26d2ASS|RmA7WIRdwBGeEX@^n^$S=%X+#RSnUcC?`0#bZ+VyL zcXF&pG?ITPyX>J>&mMA)H=?ojHzq-moiszu+DD17X&-MNXBp`V zoh@3U9-{M}q67z1U-eg81fp{|;;KX|tTPf%=MOp3kNj9Z&xL zUuPfQlQaMVV)n_;3H_FGK`K>)@Z7L^DAcwg&e(V)>!}Ak0w4itH4}-ZU0gEVrLUIH zC7><0D%=7DMR()Psf2r{VE>?gX!;Si!nA}otUoE5n!rP7?%o(B&i`qcP2~D(hF45< z9n$ybyVZzRIlzwGiUnHU40XeVOMaogs*|hF>r-9mBq`6oT>TP99Y)C(jOF${+3%3e zwB*KYfFdHy>1EE|erY{ve5X3uF5LrYj5Lh8un{9QT0T3cYu}qfmc|8h))a{cBp1Fq zol&GPcnlMMNw-)7FuF+9`9GjjlcLODssI3HL$LNxeD)o}AVTk6;s5|HpBey&felhi zYbNfLh=3lSUDik6GVe*-S_Npf9cFH@XxGZTl_%^xf@nmx{r7MW@?m% zt4xD_Jb@wr5CzMA`MrMj=vk8vJ}-mXJMw}0Kb&I7v#viRRKe2@QNxa~r%Y&^WwuFw zJnXzGH&S)^wD!!su1-Qu_08LXjL@Qh>wH&gx)*ab0icH5TKOQvNvM~YjL8uY-9&H& zXe$heQ?THWofGspNr`D=s zUV?Czb663;fCK?yjLPzpT4%5TKze`G3Jm5-gFAPdZay3qlLUAA)xG(%9^^$cAem~O zI&EP8V4TR;B0zx`g+%UI%0}!bj@ST*fFN&6Ni_ek&OPkoSa>MwEMNQMOCXK?j)1KM zL;*LUa^~?K{i}fszh^g{LwrHIU((R_*SYO|#@Az*m7npq3}5Rc{xyYX+tHe%v->XX zG>R|Ns!B$CL?R5fPrF6ng7o{wCo}7gd3;fO(LS85KCe!2I!96@S$$qzJ?VkdoY3>Y z0DvMQDlicwbz0H#_x>6MU;q;^XYHSzW~YhB&SZ!QnnC~r5&_T%h0g$XV7Qiv*!Mc# zbB#obwVV&pkH?}!VSJ3%f2+lKaEUgJi<$5_EKNB;1XyuU7Fw*=)ipoxY!D&}&o`W! zKSJX9RDeLfE(_`ut3VMBp3g1Yof|KsnmXmRn(Zya)}bhxpM6`KzaU(cw*O7XE9n<# zt$|EGmc2V|R?BP5z@TA{`>Vw{zc8ZtzH^|Iw3(y2f^pZa^4(}`P|V*mJO@s6N4NC$ z=E1I!t}%XMtJUiM?LIq}Tj)flKfT|VO&>e;gmj(vRm0UM!{>zR1sV;5rqisBdVL0I zqA>f9JmPt>t$$-=y|YU@WzDMj9B0f@K>L6IM`PtW=n30Q@@4?t`m#&EKfnIlnv_N$ zL7oZwV#dtY?@u}}Hr`8AL^HsYVLro9H_dNMi?*D~TbH;Vv3@PoneiVh{&tUCS)SU8 zf0t{uXnn>4AV2mE>m#}J%kVGt9VGTlXi%+oVD1?vx5ia22C*fHNmXARtbiazIDbHm@>lz)Tp_=AAb4Na+xKUU=9UFco8oP`!ik?> zTg|f7j104sg0Gci-&H?4YdGT)d%K42B|qHldHv?_oXxP}W#kaTn$db&<>pM|y45_K znV6X9D>5b?{oG#GrJR}JYm;JR*Ap&=l@?vTiPu#z{0{>>|7x0R;pzYdxTq-T5Jy?& zl>dKy_uINayoYo&a{+?dT#O0a_UlZ5}`_8uu|o9m)_^Y3--`q7Te zc7YSYoDT@gcDNT+lzePX>sy?RW_P*SDMG&|&|nJi=jx~3UZ|knOE(oC3fpjweY@bo z=lJbl7rA)XBWNStv&(DS%>&x1*G@^!m(H}>NonruC?Ej8*n3yrwCgB?I4 z%u-EkUr%+BL6g0bIldPCm4LCA#^fyTp8vV2=03(cakOs?HyaQE2!$A62#95{zybq* z17(kegEXxakWO%&J0_HS*v?a;*K^pNwojbL1?_}WeajwKP2YUl2oVqjFmd!BbSb&+ zX6hT);GeK^mpS8|DJsQg{-1k18Vna4+4y5e(eCI@A5T1t#3i0GyXwK-3Sx`C%n=X- zK!6}^Ig8^^mU0>WgzBI~-31rSSH`SlLgjrP5uSDQLhEt)HMs8P|6I6`t!d|?m2I@F zQ<;@kFH?+E7xy>EThe-6sqZuu(mXNRSstxEI&O*iYjzOrt-?XKeMOBlf1mH_@wfG@ z5vejRXJDPO8qXFI5NAEN&4^JRV|oiecJ%&gMD!!xU`Ks7)bM=YOGNu~JlvCt;^IBO z+10C?AYr&(avpM~CJ7}I!sCIRt;6FV)b?=e*1h>+qinWklYS{=>S6jiSa!t6POLIYh}0 z%yHm>Iez(`%6E7#D{A9Q@=q#rckJQM`F(oM#PoXAS;mK z8zv8OE$uQhkIoWqviy&+wH=jCrk_Bik6Rg#!j+z2155dM@Mof>`#7k*sj5mW!I{)0 zyvT(@*lm2enD$vcYZ3d0@mAhnqP3c7l#RIg>F1(eDSQhsX!wLgL;(>IK+Ekk-xE7Y zQv?bH^v`MO3NcHi^+A7PPnmzP!j$akcRh&{PS&P$_P7ntUwA3$rj0J<`SuSlBc;vA z8eHLXX4RSxINSDq7hWvPWky=7Gw%Wl`uUQ^s&;d}N^bTYzj57h`slKJhc^f%+-*C| zdy}$MJ9L0WpFs6I{q``TZmk8PPD>Ogi)-sB%iX{D&X|Wp@vz#gX zoW8S34;z-gJ=6mlXJwL@%-2+@?W>=rakZnH?7X5R!iEi6`l%k#{4q1K!{-fCvgT4=)<0ewjy`9bVg>Qm{siGjzpiK(OC28Qka1 z_t&4K`bZvK2Cp1+=AXc?aEhY;~<@Ra(gu0D}=y_!u_$>9JjSgU=vQks4ReT#S(FF5 z(<(UG#{=8gtv8t5%&<4B{Xd@k|qF0p~51~!(iT%}r87A*!ljD>ewvuseR zI~vR{3z`c`Lo&wg+fSrkUEbjt_B-Br;Iug0%;~$jicfXDleI!`*0nL7yG1ATAs0nI zR_|7^sp7bIXF8}oFGlTKZxFR*&C^S88@H`qyvr2ft4jur^!qeix8JXpR^r^*)0^f8 z?Cq4NineWWN+0CgVXO@GETwimwex~*;_^nz5Y-gH>E*v8H?8~a8@4NZjv~|1tPJm) zXKAwDJ;=KtZ?{_-68i!v2;1Koq|A$D&E_tL-EyJuk#6sCqUo1-8XRNkD!-Dm_rC+( z@9d3cIN@?4n0Gpr8m+}XTHo0m+z;O+U%&t$H$Vad@Bkwz^_rKlt^2ti;hEg=H_p#3 z%FQj~*rce~zvKG&J>J6Wvx@J zqSP>9$OqXEQ{Qw}*VkA7^Ar`5Wi^^DfXv_wDsTV)|NsC0|NsC0|NsC0|NsC0|NsAg z|9}7g|G)qL|NsB~0j~M&w$BUiWo47+E%nm(ZmxEgdoOy!p0%tLpaWU~Kqoz|<;&R$ zK~Hul0*WH$%DU;uq@37f1SFK2o~9^mOl2~jrfO)=%%di1XvQh%)HKrs(dv01WHVFL z4^zs1spSJ|W~22c$*A=jG|eD3lOWoF0D6y5Xwyv8*-ugGJwqURhSFpWH1vV8f>8}k zoq04=@%zW`Z1!b_u|$L{V;}pLni(VOFxKoLA=yQCV=T#*gzSxML$Z@4qJ@y1td(Tn zQb;QGoA3Ac$M5%?d(OR|=dS0Tdq3wqpU?BW-XIVo1)5)AR$0dB<#Tz;g`%hoLKBpg z4~sK;7}Ho;0oqV5;8{$9z9J6950JdvoW0OcS~kqogCop zz*Tt+0LFkZWq=jnql*(KDFU9J0EhxA#Q-c*`ecGc*%&`jo2E%g85`5qnWEi6nU&U1 zcB5V3zo!C6000pJXg)wB3-38K815Be;Z@l@b!ZV@nV4_kb9t(Q&kuJius9;H80Yb^PivpoYmuNH9+?_!DVg2PH#Ml3a{HKdV+%4hpYJ)o zSwcOv(79YNHA!U1A{kVqfwje@q6x;8`T3Q<@+-!*^{SS0CoL4JqqDSye-&6BDe?*4 zGj})GOy*-pO-`CoAoCf(+p+J(B_%D&>_k2~hOxKv442Hc^{A>8ZB`4hE_pj-$tDZsM;}i?Y(yHBamOTlJePY`#T;VbdCkRDwSB})bpQB%vvn@LB zJUMRwE^Oy512yw|PS$H7Y)U1=lIL)5d8ga}i5Io8E(|y=-c^_a3#V*Cg zTbh6Pr{=VxCOpu6_Z{go+$4v={FCi^w+g9llz?vVRl*KHJ?q;Cb(va{UQX~C$Q>F& z7pxp#5*PbHFChv#ed;#v36n7er`Nl`2WlK0rfWlvof2wi^ww4a{U)3C~ z4y4r9tn|AJ-Wi>2BSqwTC$qp#W>0jn$&7nebmt_$(-RN(Q3Jv&lPYy-D+@$0xcSE< zWT9%v;+U;a6SI*BFUR6Yt!GBqfleJIUPN=n8(*?jr_V?F#%{o+jWA`f#^dcSp9mz8lxMMs5r zN4caGs?18&QBtNn(Ee6qyUa5!8=}Wm*p*&jm7HY{T!A`xwGD{s8}HnbqCZ<=SyroD z-lT{?a7j&@+l2i#aG0<55=L-~yNhHb7_@MEJv5)lHEX>Rj5d6$LawKGmq!y_Hj~%} zrD1f4Eg_k4{YTa{8HlHsxg7nanZr@&lBiK8mZVOK5WY@WAXxfsteH?ZO_hTp|EGuT zhrAw=AeqnCXZ-OQ9t?hQQS`FJ8MWiV1CauQagSD?mbtxOP8>aF-UOBXCO&1pYoHJl z{+pnCLpcxMs511FiHe+QVf{N{E6J?B|K87Yd78&Q zXV#iqw*PZczWYtWMz($ML&xYw5PjUK9HhuRxcsJre8X$$3V7lAO}oMmd*8Yo5Q8DK zLRr=coicm7S(muNHTOLcAX9O__V~|ZKw|%e$M5VR`6V9zMeF4^p1ItS&bF~0e^KM5 zm(v8Sou-H$#|$}&XGg11isR$fD@L_@9S+H+C}IgNfd!78J^Izz6ILF~upU%cF9~d3 zSXd$Z`0x4(tYA2uy26jP|C*c}cV9KZK%d^Uo6gH0l4%^0X}Y#IPEgt;9$HmTKdY>I zg0qgw`d3H)r^}DwfC_fuas}zepeNgZcaJAT zq*`3UJ=Yos6?cx4Bn;8Ij3ebVuz(PgDc2dw$3B+gQn^Ay4Q3bCPE5dkq4rjGFOd4&8jkU8l111s{&BKxf7q?Ii}GD0+geVs$8_v z4aS9>CLKQib~~TY8UyEe<%NZ-*F^cggnpd3RK|cJvm1#>I`VdC!}?Ob_{NsEqBqU5 zxrUS36$I-4JTiSA^0!3yLM-TIoqbcUrX^_ej~KRfOSqsiB9@7KYWos*UR`e3$ zFKWyTRW&~>f%l>2?jQZ?i%nndKSy7^zT18NHsmvo7jZsINzwUUXFVwMDm>`KJm}_o zfF*CVG!axu#j8JSc+liQMA<&`oTCq9$+rmChbo*Z4?@|kuX)2T7^{QPAq z1_LL?ZS9-=-9enE)smIp1Ppq6O$L_?sJMWR2U*$98|DEuo0>A^l7`j@PN{|~VR8;2 z&fJY=G;7!3QT@L|EPP*+G-Q1*y{#lBdy4Teehsq&Yk%tdbY^!?@3uz}!2GHlPq+Oy zc&E5tR=R}hI`%>~O}g?$c@a-9l79$a6q=btq{|W&aS852Zkul+olARRzLVWJk0nDX zY1Q4=#?!X;#*IMqOo|+zq&gCv`p0 zvpIip@QYy~kJ(Q!3U$6bj^HBpFBLKI7@U>dT#t?=GW`}G+=!r>3;LyWI>kOJ(w_b? zQxf1I+95Xdxho-Nhs4WVgJ0T8x$;BC_}xk!$BmYP3%#NX1^o6E-Of23Gu>oxHc-dU zbbr=(TMp&)-v8NH0-2@ZgUdHJ_H|3Wz7e^IBw4-7vxWp3yr{%xc$69dd~sA8nPSG* zWIg0$9#Q(%YbP!+h2fML((e?T{9qdff^(*>jF;7H$ZMRigrjch6f+1-J44ytv`Fykiw|ezlNvjn%6<{>EW5d;qnvXh`lx6;x%4sEmh$ zEPaxYA}@^uGlol*R*#pi655D?ufV73jtcjYI3{5{7?NcfsqPcGbeI|xx3{bT#~VOd zvFWd>v3^}Aqu&1rA0A*_BI{(%V!CEG(_fDg#sRH(Vun6~i%~R{3(8nJ>$TxIiSXD> z|F9OzIlut+zC%;@ne}L@sompIeEPfbvz3`HUY9eO*yQH90wPA;(Y?Xo>i4;OLRH;i zsi4_LY3WlGdV)k))M7W^xNwF;MFwG1E|f2}`rWocM%~epfqcIxr#2Sz%2_}t<)CKZ z{zPe3`Q|`Jy?E%4Ge59qFWosVOrhjV4$6 zb%tO%p7u0`O5(5a+MH?Dko+0DRVxGDKI?dAE3-wHH%QfNwT4(j?^%V(d(8E2v7*XK zRo5eSvgGk`MbBCEMYlhczBar^+Wxktq`)FT|HV4o2s$!8OvJm&PqSz)KPWsXPT6hN z;KE(7VECAx2@$bgO)EzkjOcl%kFnOOD!mpX3t2JZ{0j;@Ifj!;zteb{npH>ySEf>S zlXZiIAuj>7c%g{T@FABSpT*^@<^-4aWF8)sa06ahZIH~@G%3SiLl;}ZbP3TSabs1w zHnnBv+jN*sZSH0QSKtOmwWR)pwY9%P4uO?m)a2m`!jr*15Q_@4G>}wIkV)x%^ixaQkIen!fsj4ATygT(=8TXP0L`O1ALy>Gp_Pk7&!zfHWSOROULep z{v+09yXN*G&F*ETj$xkh>o7q|Q=obvr3`5Sdh2w|vl-pZk7y1ta>l%{8h}Q`PPHGm zXTuA*vYmEzriAs=ErM|?2ySYQ8AC#;M+RjAqY@~b;IuW7Fep{=CRE|y|rbtPoqNPkB)ZN|tMtl7sYC7EZ zOxCK~G}_$z;o!aQ@dzM*Wy3TBa@<)o->D?hjQhbe}Fg+i4{rLSB|F`<@p%3@HPBLqXB!Cfo@s@6;7-P_gUjP$HDp(sFzW zVjzK4ll#?=;HeY%ce{M2KT!Qaq26!306 zKYZ8J@^R645%V@t3RrmgQ`9p$Y#a$%Up+0rJ=g@%x2*f0G_~wnT6BW`_!}b@f5Who zaKVMW^iQh`<+?T_gRPYU2?;c$nzQyhMv2?A6W|U6_Jnkx56P$3`CMx3bIlge!U1?R zOSotqw^Dm|AlfkPQ1+Q<8)Kvf>x(CU;wOIkr6$Yu&2@I4@i+1d=M?<( zNi(P7?`imjNtn@~`FA&_dG-``^@oX}U!IfMvlpI)Tr};XZ#%psSKwY`TT*^Y^OEUf z_xn7fIyYGd`-uTp%oGGgU1ae7djIt%;lAREZ^8)*pyKZbTPFLnVlD=Q5=r$Z}N}5$L5%JdSnW6XN-kB z6IvIY2yVD_Z*>wnL@BO(aO-@iEJnrsfEPS9DIx*LgFy<3L_rp-4G(%G%c{zl*E8Nt z^46}P9(-=NuIb7Bf!l&>@B4P7yRI+!qxEF=qrQ$214;t3TY2Yu;uUED+B(@$co|nftXf|{f@>N@AjtjKiqx#8udy& z?Z?GR+K@a32(W!cn*PCqP-tIPk2L!oqDY~bVBuaz!JkIqp@Vr+Sh2e1vyL9kJgxZf zr<|57u`U4*-~IiFUVY4!%+s+ih2M&dHnDWT)Q#K#mLU?iiM%`0!5#$dwnMY;<1h6R zlPN_92q>+uiqDiHF+{i1A`Vd^LTCq|N<^a=;{Vf324{pf<3VQ)*$SvB^mYPpSrBrb zWJAv&1#98-V@rkk9APS1)Im|C8nu@Al|YaKfnFC$y6QU&3vudEi)&|D8b5Kdvht6a zWee2ovF-g#UTYtHC;8#Gk<^aP3%wu8gb1w7-9T{9mLk)(`zUd46g$Y1FlqpfrKS4D zt`0SLn}nbri&5w?oc>Z$B{|7iQj$sXp`cF8JZu5{A=a-nRVf*D1Kce^$dJVW;TOeN z*kjDiS^W=D30Bu#GMR;Jfyao5C2VY!wIMJDlj3q_!#Lbbm1PZuykQxIl(KF8hAe;K zI?kt&F3~a$`AX{sEgm(RRSDFcy+ z?0uVU%W-{hL5Wrz7C*Q5JLbyUz4Wzz?7^Z!_rdt9MkhTRnCQy2@66O>EP(&C0=wM* z%NT@!9tKPCc49#vZJF5J+JLdI;_uQ^GMGhtzjIGDK7Pc?Sv6xQRUz6)p_O854x zbWyrmQQG7|yn(9s$1SpCHBunr>nj%Y(ZyKcW^HF}XS&dZvXJjyzsJp<3JN02XB^eK zzNJR<2}Q?qK9bNF2`bmT>Liq8o>eaS$-tQ$0R;$Lv`G2142p-I=j~H)|F4bC6eeK1 z(Sbh$OaeB0%RBKcvRpHGhVXe3Bt%egKr6ClTPSB9r44wpKF4@;{r%g;g}p!n+FX?U z@7IQm+v(9HDaOX6JeN{xn_T+|w16SLERW*`%uMjP|8(S;3|UT1nS-JY7)2kIB!O-s z2r3cI>%|<&H=<%j_p)gDogFKGzdO{etW>VX8 zs0Dd+9|gZ7-Rh1)OdIcEnex+^aD2!qrnH|D2VE_fGQ=@U{i3YB zU#leyIq=p}nwKO7hSi9YcM+|Y5(zJSw%vN9VOcJ)3t{*WvU( zwM@?*ol+$ID&hS6ZAoMi5zWt_-30o*xU8y$v%;WHB4E*u$-#Q2e-Ublq3zD^kIKf} zzU$JNE?e2YhJZ`j(Eks6SRDd~SqYyP&BS0i=so;$fUCl0rrj3gQ&CXGulb-4B)K_V z%`Z2vJyr|JuQy?}W4M0Y;b3AFB~eDL@ZoUaSmcb^Z6DV{8(6~X3YWg1ZO^E>yX57S zHm8Ls)@0ziB#UJGK5@&rd1rEHK1lD%%Y==4vmG%5*h}r*XqE@gxx!cI*y;&uvap;x<}$$|t?ncVIo z+|3{D=lqT1%@~nqGq@xRtyH^Bh`HaCa_Vcaa6ddf$AROwagzu2UuokxuA~bwTtI}= zt{GPIZzM5XP{NB^ZX<+}xDn;z%6KI~)%ac^8~s;tM-iV6ogXH9N@DWc(nQ&&4aoYV zPBKdQSg0Ii_Fjl>3FA+)epCU{c`17}Wo<)R2`9H+TqwWvS8gTQ LzeISQ+#>WpKr3c0 diff --git a/tests/python/shared/assets/cvat_db/data.json b/tests/python/shared/assets/cvat_db/data.json index 53863fa94fcc..9e9a7ee8e5f7 100644 --- a/tests/python/shared/assets/cvat_db/data.json +++ b/tests/python/shared/assets/cvat_db/data.json @@ -1213,6 +1213,17 @@ "disabled_frames": "[]" } }, +{ + "model": "engine.validationlayout", + "pk": 4, + "fields": { + "task_data": 29, + "mode": "gt_pool", + "frames_per_job_count": 2, + "frames": "8,9", + "disabled_frames": "[]" + } +}, { "model": "engine.data", "pk": 2, @@ -1650,6 +1661,25 @@ "deleted_frames": "[]" } }, +{ + "model": "engine.data", + "pk": 29, + "fields": { + "chunk_size": 72, + "size": 10, + "image_quality": 70, + "start_frame": 0, + "stop_frame": 9, + "frame_filter": "", + "compressed_chunk_type": "imageset", + "original_chunk_type": "imageset", + "storage_method": "cache", + "storage": "local", + "cloud_storage": null, + "sorting_method": "random", + "deleted_frames": "[]" + } +}, { "model": "engine.video", "pk": 1, @@ -3880,6 +3910,136 @@ "real_frame": 0 } }, +{ + "model": "engine.image", + "pk": 523, + "fields": { + "data": 29, + "path": "32.png", + "frame": 0, + "width": 936, + "height": 182, + "is_placeholder": true, + "real_frame": 8 + } +}, +{ + "model": "engine.image", + "pk": 524, + "fields": { + "data": 29, + "path": "30.png", + "frame": 1, + "width": 810, + "height": 399, + "is_placeholder": false, + "real_frame": 0 + } +}, +{ + "model": "engine.image", + "pk": 525, + "fields": { + "data": 29, + "path": "31.png", + "frame": 2, + "width": 916, + "height": 158, + "is_placeholder": false, + "real_frame": 0 + } +}, +{ + "model": "engine.image", + "pk": 526, + "fields": { + "data": 29, + "path": "35.png", + "frame": 3, + "width": 721, + "height": 705, + "is_placeholder": true, + "real_frame": 9 + } +}, +{ + "model": "engine.image", + "pk": 527, + "fields": { + "data": 29, + "path": "34.png", + "frame": 4, + "width": 231, + "height": 121, + "is_placeholder": false, + "real_frame": 0 + } +}, +{ + "model": "engine.image", + "pk": 528, + "fields": { + "data": 29, + "path": "35.png", + "frame": 5, + "width": 721, + "height": 705, + "is_placeholder": true, + "real_frame": 9 + } +}, +{ + "model": "engine.image", + "pk": 529, + "fields": { + "data": 29, + "path": "32.png", + "frame": 6, + "width": 936, + "height": 182, + "is_placeholder": true, + "real_frame": 8 + } +}, +{ + "model": "engine.image", + "pk": 530, + "fields": { + "data": 29, + "path": "33.png", + "frame": 7, + "width": 783, + "height": 433, + "is_placeholder": false, + "real_frame": 0 + } +}, +{ + "model": "engine.image", + "pk": 531, + "fields": { + "data": 29, + "path": "32.png", + "frame": 8, + "width": 936, + "height": 182, + "is_placeholder": false, + "real_frame": 0 + } +}, +{ + "model": "engine.image", + "pk": 532, + "fields": { + "data": 29, + "path": "35.png", + "frame": 9, + "width": 721, + "height": 705, + "is_placeholder": false, + "real_frame": 0 + } +}, { "model": "engine.project", "pk": 1, @@ -4206,7 +4366,8 @@ "subset": "", "organization": 1, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 } }, { @@ -4234,7 +4395,8 @@ "subset": "", "organization": null, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 } }, { @@ -4260,7 +4422,8 @@ "subset": "", "organization": null, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 } }, { @@ -4288,7 +4451,8 @@ "subset": "", "organization": 2, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 } }, { @@ -4316,7 +4480,8 @@ "subset": "", "organization": null, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 } }, { @@ -4344,7 +4509,8 @@ "subset": "", "organization": null, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 } }, { @@ -4372,7 +4538,8 @@ "subset": "Train", "organization": 2, "source_storage": 4, - "target_storage": 2 + "target_storage": 2, + "consensus_replicas": 0 } }, { @@ -4398,7 +4565,8 @@ "subset": "", "organization": null, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 } }, { @@ -4426,7 +4594,8 @@ "subset": "", "organization": 2, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 } }, { @@ -4452,7 +4621,8 @@ "subset": "", "organization": 2, "source_storage": 7, - "target_storage": 8 + "target_storage": 8, + "consensus_replicas": 0 } }, { @@ -4478,7 +4648,8 @@ "subset": "", "organization": null, "source_storage": 15, - "target_storage": 16 + "target_storage": 16, + "consensus_replicas": 0 } }, { @@ -4506,7 +4677,8 @@ "subset": "", "organization": 2, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 } }, { @@ -4532,7 +4704,8 @@ "subset": "", "organization": 2, "source_storage": 23, - "target_storage": 24 + "target_storage": 24, + "consensus_replicas": 0 } }, { @@ -4558,7 +4731,8 @@ "subset": "", "organization": null, "source_storage": 25, - "target_storage": 26 + "target_storage": 26, + "consensus_replicas": 0 } }, { @@ -4584,7 +4758,8 @@ "subset": "", "organization": null, "source_storage": 29, - "target_storage": 30 + "target_storage": 30, + "consensus_replicas": 0 } }, { @@ -4610,7 +4785,8 @@ "subset": "", "organization": null, "source_storage": 33, - "target_storage": 34 + "target_storage": 34, + "consensus_replicas": 0 } }, { @@ -4636,7 +4812,8 @@ "subset": "Train", "organization": 2, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 } }, { @@ -4662,7 +4839,8 @@ "subset": "", "organization": null, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 } }, { @@ -4688,7 +4866,8 @@ "subset": "Train", "organization": null, "source_storage": 37, - "target_storage": 38 + "target_storage": 38, + "consensus_replicas": 0 } }, { @@ -4714,7 +4893,8 @@ "subset": "Validation", "organization": null, "source_storage": 39, - "target_storage": 40 + "target_storage": 40, + "consensus_replicas": 0 } }, { @@ -4742,7 +4922,8 @@ "subset": "", "organization": 2, "source_storage": 45, - "target_storage": 46 + "target_storage": 46, + "consensus_replicas": 0 } }, { @@ -4770,7 +4951,8 @@ "subset": "", "organization": 2, "source_storage": 47, - "target_storage": 48 + "target_storage": 48, + "consensus_replicas": 0 } }, { @@ -4796,7 +4978,8 @@ "subset": "", "organization": 2, "source_storage": 49, - "target_storage": 50 + "target_storage": 50, + "consensus_replicas": 0 } }, { @@ -4822,7 +5005,35 @@ "subset": "", "organization": 2, "source_storage": null, - "target_storage": null + "target_storage": null, + "consensus_replicas": 0 + } +}, +{ + "model": "engine.task", + "pk": 30, + "fields": { + "created_date": "2025-01-14T17:19:03.665Z", + "updated_date": "2025-01-14T17:26:01.139Z", + "project": null, + "name": "task with consensus and honeypots", + "mode": "annotation", + "owner": [ + "user2" + ], + "assignee": null, + "assignee_updated_date": null, + "bug_tracker": "", + "overlap": 0, + "segment_size": 0, + "status": "annotation", + "data": 29, + "dimension": "2d", + "subset": "", + "organization": 2, + "source_storage": 51, + "target_storage": 52, + "consensus_replicas": 3 } }, { @@ -5753,6 +5964,54 @@ "file": "/home/django/data/data/27/raw/1.png" } }, +{ + "model": "engine.clientfile", + "pk": 464, + "fields": { + "data": 29, + "file": "/home/django/data/data/29/raw/30.png" + } +}, +{ + "model": "engine.clientfile", + "pk": 465, + "fields": { + "data": 29, + "file": "/home/django/data/data/29/raw/31.png" + } +}, +{ + "model": "engine.clientfile", + "pk": 466, + "fields": { + "data": 29, + "file": "/home/django/data/data/29/raw/32.png" + } +}, +{ + "model": "engine.clientfile", + "pk": 467, + "fields": { + "data": 29, + "file": "/home/django/data/data/29/raw/33.png" + } +}, +{ + "model": "engine.clientfile", + "pk": 468, + "fields": { + "data": 29, + "file": "/home/django/data/data/29/raw/34.png" + } +}, +{ + "model": "engine.clientfile", + "pk": 469, + "fields": { + "data": 29, + "file": "/home/django/data/data/29/raw/35.png" + } +}, { "model": "engine.relatedfile", "pk": 1, @@ -6172,6 +6431,42 @@ "frames": "[]" } }, +{ + "model": "engine.segment", + "pk": 42, + "fields": { + "task": 30, + "start_frame": 0, + "stop_frame": 4, + "chunks_updated_date": "2025-01-14T17:19:04.053Z", + "type": "range", + "frames": "[]" + } +}, +{ + "model": "engine.segment", + "pk": 43, + "fields": { + "task": 30, + "start_frame": 5, + "stop_frame": 7, + "chunks_updated_date": "2025-01-14T17:19:04.090Z", + "type": "range", + "frames": "[]" + } +}, +{ + "model": "engine.segment", + "pk": 44, + "fields": { + "task": 30, + "start_frame": 8, + "stop_frame": 9, + "chunks_updated_date": "2025-01-14T17:19:04.161Z", + "type": "range", + "frames": "[]" + } +}, { "model": "engine.job", "pk": 2, @@ -6186,7 +6481,8 @@ "status": "annotation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6203,7 +6499,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6218,7 +6515,8 @@ "status": "annotation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6233,7 +6531,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6250,7 +6549,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6267,7 +6567,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6282,7 +6583,8 @@ "status": "validation", "stage": "validation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6297,7 +6599,8 @@ "status": "validation", "stage": "acceptance", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6312,7 +6615,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6329,7 +6633,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6344,7 +6649,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6359,7 +6665,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6374,7 +6681,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6389,7 +6697,8 @@ "status": "annotation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6404,7 +6713,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6419,7 +6729,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6434,7 +6745,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6449,7 +6761,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6464,7 +6777,8 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6479,7 +6793,8 @@ "status": "validation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6494,7 +6809,8 @@ "status": "validation", "stage": "acceptance", "state": "completed", - "type": "ground_truth" + "type": "ground_truth", + "parent_job": null } }, { @@ -6509,7 +6825,8 @@ "status": "completed", "stage": "acceptance", "state": "completed", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6524,7 +6841,8 @@ "status": "completed", "stage": "acceptance", "state": "completed", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6539,7 +6857,8 @@ "status": "completed", "stage": "acceptance", "state": "completed", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6554,7 +6873,8 @@ "status": "completed", "stage": "acceptance", "state": "completed", - "type": "ground_truth" + "type": "ground_truth", + "parent_job": null } }, { @@ -6569,7 +6889,8 @@ "status": "annotation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6584,7 +6905,8 @@ "status": "annotation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6599,7 +6921,8 @@ "status": "annotation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6614,7 +6937,8 @@ "status": "annotation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6629,7 +6953,8 @@ "status": "annotation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6644,7 +6969,8 @@ "status": "annotation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6659,7 +6985,8 @@ "status": "annotation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6674,7 +7001,8 @@ "status": "annotation", "stage": "annotation", "state": "new", - "type": "annotation" + "type": "annotation", + "parent_job": null } }, { @@ -6689,7 +7017,152 @@ "status": "annotation", "stage": "annotation", "state": "in progress", - "type": "ground_truth" + "type": "ground_truth", + "parent_job": null + } +}, +{ + "model": "engine.job", + "pk": 42, + "fields": { + "created_date": "2025-01-14T17:19:04.062Z", + "updated_date": "2025-01-14T17:25:16.549Z", + "segment": 42, + "assignee": null, + "assignee_updated_date": null, + "status": "annotation", + "stage": "annotation", + "state": "in progress", + "type": "annotation", + "parent_job": null + } +}, +{ + "model": "engine.job", + "pk": 43, + "fields": { + "created_date": "2025-01-14T17:19:04.077Z", + "updated_date": "2025-01-14T17:26:01.233Z", + "segment": 42, + "assignee": null, + "assignee_updated_date": null, + "status": "annotation", + "stage": "annotation", + "state": "in progress", + "type": "consensus", + "parent_job": 42 + } +}, +{ + "model": "engine.job", + "pk": 44, + "fields": { + "created_date": "2025-01-14T17:19:04.082Z", + "updated_date": "2025-01-14T17:19:51.388Z", + "segment": 42, + "assignee": null, + "assignee_updated_date": null, + "status": "annotation", + "stage": "annotation", + "state": "new", + "type": "consensus", + "parent_job": 42 + } +}, +{ + "model": "engine.job", + "pk": 45, + "fields": { + "created_date": "2025-01-14T17:19:04.086Z", + "updated_date": "2025-01-14T17:19:51.388Z", + "segment": 42, + "assignee": null, + "assignee_updated_date": null, + "status": "annotation", + "stage": "annotation", + "state": "new", + "type": "consensus", + "parent_job": 42 + } +}, +{ + "model": "engine.job", + "pk": 46, + "fields": { + "created_date": "2025-01-14T17:19:04.091Z", + "updated_date": "2025-01-14T17:24:20.174Z", + "segment": 43, + "assignee": null, + "assignee_updated_date": null, + "status": "annotation", + "stage": "annotation", + "state": "in progress", + "type": "annotation", + "parent_job": null + } +}, +{ + "model": "engine.job", + "pk": 47, + "fields": { + "created_date": "2025-01-14T17:19:04.095Z", + "updated_date": "2025-01-14T17:23:55.551Z", + "segment": 43, + "assignee": null, + "assignee_updated_date": null, + "status": "annotation", + "stage": "annotation", + "state": "in progress", + "type": "consensus", + "parent_job": 46 + } +}, +{ + "model": "engine.job", + "pk": 48, + "fields": { + "created_date": "2025-01-14T17:19:04.099Z", + "updated_date": "2025-01-14T17:19:51.388Z", + "segment": 43, + "assignee": null, + "assignee_updated_date": null, + "status": "annotation", + "stage": "annotation", + "state": "new", + "type": "consensus", + "parent_job": 46 + } +}, +{ + "model": "engine.job", + "pk": 49, + "fields": { + "created_date": "2025-01-14T17:19:04.103Z", + "updated_date": "2025-01-14T17:24:51.333Z", + "segment": 43, + "assignee": null, + "assignee_updated_date": null, + "status": "annotation", + "stage": "annotation", + "state": "in progress", + "type": "consensus", + "parent_job": 46 + } +}, +{ + "model": "engine.job", + "pk": 50, + "fields": { + "created_date": "2025-01-14T17:19:04.163Z", + "updated_date": "2025-01-14T17:21:56.768Z", + "segment": 44, + "assignee": null, + "assignee_updated_date": null, + "status": "annotation", + "stage": "annotation", + "state": "in progress", + "type": "ground_truth", + "parent_job": null } }, { @@ -7532,6 +8005,30 @@ "parent": null } }, +{ + "model": "engine.label", + "pk": 78, + "fields": { + "task": 30, + "project": null, + "name": "cat", + "color": "#6080c0", + "type": "any", + "parent": null + } +}, +{ + "model": "engine.label", + "pk": 79, + "fields": { + "task": 30, + "project": null, + "name": "dog", + "color": "#406040", + "type": "any", + "parent": null + } +}, { "model": "engine.skeleton", "pk": 1, @@ -7746,10 +8243,34 @@ }, { "model": "engine.attributespec", - "pk": 15, + "pk": 15, + "fields": { + "label": 77, + "name": "id", + "mutable": false, + "input_type": "text", + "default_value": "", + "values": "" + } +}, +{ + "model": "engine.attributespec", + "pk": 16, + "fields": { + "label": 78, + "name": "src", + "mutable": false, + "input_type": "text", + "default_value": "", + "values": "" + } +}, +{ + "model": "engine.attributespec", + "pk": 17, "fields": { - "label": 77, - "name": "id", + "label": 79, + "name": "src", "mutable": false, "input_type": "text", "default_value": "", @@ -10828,6 +11349,186 @@ "parent": null } }, +{ + "model": "engine.labeledshape", + "pk": 191, + "fields": { + "job": 50, + "label": 78, + "frame": 8, + "group": 0, + "source": "Ground truth", + "type": "rectangle", + "occluded": false, + "outside": false, + "z_order": 0, + "points": "160.71132684643453,59.61108186333331,254.8470583598446,111.65360009838878", + "rotation": 0.0, + "parent": null + } +}, +{ + "model": "engine.labeledshape", + "pk": 192, + "fields": { + "job": 50, + "label": 78, + "frame": 8, + "group": 0, + "source": "Ground truth", + "type": "rectangle", + "occluded": false, + "outside": false, + "z_order": 0, + "points": "434.6998787309949,78.74436062622226,502.0490199763608,127.72555425921564", + "rotation": 0.0, + "parent": null + } +}, +{ + "model": "engine.labeledshape", + "pk": 193, + "fields": { + "job": 50, + "label": 78, + "frame": 9, + "group": 0, + "source": "Ground truth", + "type": "rectangle", + "occluded": false, + "outside": false, + "z_order": 0, + "points": "245.67336843180783,162.98163621791718,334.90269485918907,215.98915686784676", + "rotation": 0.0, + "parent": null + } +}, +{ + "model": "engine.labeledshape", + "pk": 194, + "fields": { + "job": 46, + "label": 78, + "frame": 6, + "group": 0, + "source": "manual", + "type": "rectangle", + "occluded": false, + "outside": false, + "z_order": 0, + "points": "472.58377068151276,63.05507204065361,535.3409250237855,110.50560337261595", + "rotation": 0.0, + "parent": null + } +}, +{ + "model": "engine.labeledshape", + "pk": 195, + "fields": { + "job": 47, + "label": 78, + "frame": 5, + "group": 0, + "source": "manual", + "type": "rectangle", + "occluded": false, + "outside": false, + "z_order": 0, + "points": "183.389531668141,181.09253910664302,247.88201512555497,226.14893165908325", + "rotation": 0.0, + "parent": null + } +}, +{ + "model": "engine.labeledshape", + "pk": 196, + "fields": { + "job": 47, + "label": 78, + "frame": 5, + "group": 0, + "source": "manual", + "type": "rectangle", + "occluded": false, + "outside": false, + "z_order": 0, + "points": "344.1790109729245,311.8444233764694,391.8857795578606,376.3369068338834", + "rotation": 0.0, + "parent": null + } +}, +{ + "model": "engine.labeledshape", + "pk": 197, + "fields": { + "job": 49, + "label": 78, + "frame": 5, + "group": 0, + "source": "manual", + "type": "rectangle", + "occluded": false, + "outside": false, + "z_order": 0, + "points": "169.25419282815892,210.24667546410456,239.0474283505664,239.4008118215661", + "rotation": 0.0, + "parent": null + } +}, +{ + "model": "engine.labeledshape", + "pk": 198, + "fields": { + "job": 49, + "label": 78, + "frame": 5, + "group": 0, + "source": "manual", + "type": "rectangle", + "occluded": false, + "outside": false, + "z_order": 0, + "points": "336.22788287543517,361.3181093164021,398.9534489778507,424.9271340963169", + "rotation": 0.0, + "parent": null + } +}, +{ + "model": "engine.labeledshape", + "pk": 199, + "fields": { + "job": 42, + "label": 78, + "frame": 0, + "group": 0, + "source": "manual", + "type": "rectangle", + "occluded": false, + "outside": false, + "z_order": 0, + "points": "260.20437641345416,64.96839991694105,339.7988160670684,103.23495744271713", + "rotation": 0.0, + "parent": null + } +}, +{ + "model": "engine.labeledshape", + "pk": 200, + "fields": { + "job": 43, + "label": 78, + "frame": 0, + "group": 0, + "source": "manual", + "type": "rectangle", + "occluded": false, + "outside": false, + "z_order": 0, + "points": "236.47911074747208,63.43773761591001,335.20682916397527,123.1335673561207", + "rotation": 0.0, + "parent": null + } +}, { "model": "engine.labeledshapeattributeval", "pk": 1, @@ -12169,6 +12870,96 @@ "shape": 187 } }, +{ + "model": "engine.labeledshapeattributeval", + "pk": 151, + "fields": { + "spec": 16, + "value": "gt job frame 1 (32) ann 1", + "shape": 191 + } +}, +{ + "model": "engine.labeledshapeattributeval", + "pk": 152, + "fields": { + "spec": 16, + "value": "gt job frame 1 (32) ann 2", + "shape": 192 + } +}, +{ + "model": "engine.labeledshapeattributeval", + "pk": 153, + "fields": { + "spec": 16, + "value": "gt job frame 2 (35) ann 1", + "shape": 193 + } +}, +{ + "model": "engine.labeledshapeattributeval", + "pk": 155, + "fields": { + "spec": 16, + "value": "j2 r1 frame 1 (35) ann 1", + "shape": 195 + } +}, +{ + "model": "engine.labeledshapeattributeval", + "pk": 156, + "fields": { + "spec": 16, + "value": "j2 r1 frame 1 (35) ann 2", + "shape": 196 + } +}, +{ + "model": "engine.labeledshapeattributeval", + "pk": 157, + "fields": { + "spec": 16, + "value": "j2 frame 2 (32) ann 1", + "shape": 194 + } +}, +{ + "model": "engine.labeledshapeattributeval", + "pk": 158, + "fields": { + "spec": 16, + "value": "j2 r3 frame 1 (35) ann 1", + "shape": 197 + } +}, +{ + "model": "engine.labeledshapeattributeval", + "pk": 159, + "fields": { + "spec": 16, + "value": "j2 r3 frame 1 (35) ann 2", + "shape": 198 + } +}, +{ + "model": "engine.labeledshapeattributeval", + "pk": 160, + "fields": { + "spec": 16, + "value": "j1 frame 1 (32) ann 1", + "shape": 199 + } +}, +{ + "model": "engine.labeledshapeattributeval", + "pk": 161, + "fields": { + "spec": 16, + "value": "j1 r1 frame 1 (32) ann 1", + "shape": 200 + } +}, { "model": "engine.labeledtrack", "pk": 1, @@ -13519,6 +14310,22 @@ "cloud_storage": null } }, +{ + "model": "engine.storage", + "pk": 51, + "fields": { + "location": "local", + "cloud_storage": null + } +}, +{ + "model": "engine.storage", + "pk": 52, + "fields": { + "location": "local", + "cloud_storage": null + } +}, { "model": "webhooks.webhook", "pk": 1, @@ -18731,6 +19538,30 @@ "max_validations_per_job": 0 } }, +{ + "model": "quality_control.qualitysettings", + "pk": 25, + "fields": { + "task": 30, + "iou_threshold": 0.4, + "oks_sigma": 0.09, + "line_thickness": 0.01, + "low_overlap_threshold": 0.8, + "point_size_base": "group_bbox_size", + "compare_line_orientation": true, + "line_orientation_threshold": 0.1, + "compare_groups": true, + "group_match_threshold": 0.5, + "check_covered_annotations": true, + "object_visibility_threshold": 0.05, + "panoptic_comparison": true, + "compare_attributes": true, + "empty_is_annotated": false, + "target_metric": "accuracy", + "target_metric_threshold": 0.7, + "max_validations_per_job": 0 + } +}, { "model": "admin.logentry", "pk": 1, diff --git a/tests/python/shared/assets/jobs.json b/tests/python/shared/assets/jobs.json index d406fcfc637a..7e0ca9331e5f 100644 --- a/tests/python/shared/assets/jobs.json +++ b/tests/python/shared/assets/jobs.json @@ -1,5 +1,5 @@ { - "count": 34, + "count": 43, "next": null, "previous": null, "results": [ @@ -7,6 +7,403 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, + "created_date": "2025-01-14T17:19:04.163000Z", + "data_chunk_size": 72, + "data_compressed_chunk_type": "imageset", + "data_original_chunk_type": "imageset", + "dimension": "2d", + "frame_count": 2, + "guide_id": null, + "id": 50, + "issues": { + "count": 0, + "url": "http://localhost:8080/api/issues?job_id=50" + }, + "labels": { + "url": "http://localhost:8080/api/labels?job_id=50" + }, + "mode": "annotation", + "organization": 2, + "parent_job_id": null, + "project_id": null, + "source_storage": { + "cloud_storage_id": null, + "id": 51, + "location": "local" + }, + "stage": "annotation", + "start_frame": 8, + "state": "in progress", + "status": "annotation", + "stop_frame": 9, + "target_storage": { + "cloud_storage_id": null, + "id": 52, + "location": "local" + }, + "task_id": 30, + "type": "ground_truth", + "updated_date": "2025-01-14T17:21:56.768000Z", + "url": "http://localhost:8080/api/jobs/50" + }, + { + "assignee": null, + "assignee_updated_date": null, + "bug_tracker": null, + "consensus_replicas": 0, + "created_date": "2025-01-14T17:19:04.103000Z", + "data_chunk_size": 72, + "data_compressed_chunk_type": "imageset", + "data_original_chunk_type": "imageset", + "dimension": "2d", + "frame_count": 3, + "guide_id": null, + "id": 49, + "issues": { + "count": 0, + "url": "http://localhost:8080/api/issues?job_id=49" + }, + "labels": { + "url": "http://localhost:8080/api/labels?job_id=49" + }, + "mode": "annotation", + "organization": 2, + "parent_job_id": 46, + "project_id": null, + "source_storage": { + "cloud_storage_id": null, + "id": 51, + "location": "local" + }, + "stage": "annotation", + "start_frame": 5, + "state": "in progress", + "status": "annotation", + "stop_frame": 7, + "target_storage": { + "cloud_storage_id": null, + "id": 52, + "location": "local" + }, + "task_id": 30, + "type": "consensus", + "updated_date": "2025-01-14T17:24:51.333000Z", + "url": "http://localhost:8080/api/jobs/49" + }, + { + "assignee": null, + "assignee_updated_date": null, + "bug_tracker": null, + "consensus_replicas": 0, + "created_date": "2025-01-14T17:19:04.099000Z", + "data_chunk_size": 72, + "data_compressed_chunk_type": "imageset", + "data_original_chunk_type": "imageset", + "dimension": "2d", + "frame_count": 3, + "guide_id": null, + "id": 48, + "issues": { + "count": 0, + "url": "http://localhost:8080/api/issues?job_id=48" + }, + "labels": { + "url": "http://localhost:8080/api/labels?job_id=48" + }, + "mode": "annotation", + "organization": 2, + "parent_job_id": 46, + "project_id": null, + "source_storage": { + "cloud_storage_id": null, + "id": 51, + "location": "local" + }, + "stage": "annotation", + "start_frame": 5, + "state": "new", + "status": "annotation", + "stop_frame": 7, + "target_storage": { + "cloud_storage_id": null, + "id": 52, + "location": "local" + }, + "task_id": 30, + "type": "consensus", + "updated_date": "2025-01-14T17:19:51.388000Z", + "url": "http://localhost:8080/api/jobs/48" + }, + { + "assignee": null, + "assignee_updated_date": null, + "bug_tracker": null, + "consensus_replicas": 0, + "created_date": "2025-01-14T17:19:04.095000Z", + "data_chunk_size": 72, + "data_compressed_chunk_type": "imageset", + "data_original_chunk_type": "imageset", + "dimension": "2d", + "frame_count": 3, + "guide_id": null, + "id": 47, + "issues": { + "count": 0, + "url": "http://localhost:8080/api/issues?job_id=47" + }, + "labels": { + "url": "http://localhost:8080/api/labels?job_id=47" + }, + "mode": "annotation", + "organization": 2, + "parent_job_id": 46, + "project_id": null, + "source_storage": { + "cloud_storage_id": null, + "id": 51, + "location": "local" + }, + "stage": "annotation", + "start_frame": 5, + "state": "in progress", + "status": "annotation", + "stop_frame": 7, + "target_storage": { + "cloud_storage_id": null, + "id": 52, + "location": "local" + }, + "task_id": 30, + "type": "consensus", + "updated_date": "2025-01-14T17:23:55.551000Z", + "url": "http://localhost:8080/api/jobs/47" + }, + { + "assignee": null, + "assignee_updated_date": null, + "bug_tracker": null, + "consensus_replicas": 3, + "created_date": "2025-01-14T17:19:04.091000Z", + "data_chunk_size": 72, + "data_compressed_chunk_type": "imageset", + "data_original_chunk_type": "imageset", + "dimension": "2d", + "frame_count": 3, + "guide_id": null, + "id": 46, + "issues": { + "count": 0, + "url": "http://localhost:8080/api/issues?job_id=46" + }, + "labels": { + "url": "http://localhost:8080/api/labels?job_id=46" + }, + "mode": "annotation", + "organization": 2, + "parent_job_id": null, + "project_id": null, + "source_storage": { + "cloud_storage_id": null, + "id": 51, + "location": "local" + }, + "stage": "annotation", + "start_frame": 5, + "state": "in progress", + "status": "annotation", + "stop_frame": 7, + "target_storage": { + "cloud_storage_id": null, + "id": 52, + "location": "local" + }, + "task_id": 30, + "type": "annotation", + "updated_date": "2025-01-14T17:24:20.174000Z", + "url": "http://localhost:8080/api/jobs/46" + }, + { + "assignee": null, + "assignee_updated_date": null, + "bug_tracker": null, + "consensus_replicas": 0, + "created_date": "2025-01-14T17:19:04.086000Z", + "data_chunk_size": 72, + "data_compressed_chunk_type": "imageset", + "data_original_chunk_type": "imageset", + "dimension": "2d", + "frame_count": 5, + "guide_id": null, + "id": 45, + "issues": { + "count": 0, + "url": "http://localhost:8080/api/issues?job_id=45" + }, + "labels": { + "url": "http://localhost:8080/api/labels?job_id=45" + }, + "mode": "annotation", + "organization": 2, + "parent_job_id": 42, + "project_id": null, + "source_storage": { + "cloud_storage_id": null, + "id": 51, + "location": "local" + }, + "stage": "annotation", + "start_frame": 0, + "state": "new", + "status": "annotation", + "stop_frame": 4, + "target_storage": { + "cloud_storage_id": null, + "id": 52, + "location": "local" + }, + "task_id": 30, + "type": "consensus", + "updated_date": "2025-01-14T17:19:51.388000Z", + "url": "http://localhost:8080/api/jobs/45" + }, + { + "assignee": null, + "assignee_updated_date": null, + "bug_tracker": null, + "consensus_replicas": 0, + "created_date": "2025-01-14T17:19:04.082000Z", + "data_chunk_size": 72, + "data_compressed_chunk_type": "imageset", + "data_original_chunk_type": "imageset", + "dimension": "2d", + "frame_count": 5, + "guide_id": null, + "id": 44, + "issues": { + "count": 0, + "url": "http://localhost:8080/api/issues?job_id=44" + }, + "labels": { + "url": "http://localhost:8080/api/labels?job_id=44" + }, + "mode": "annotation", + "organization": 2, + "parent_job_id": 42, + "project_id": null, + "source_storage": { + "cloud_storage_id": null, + "id": 51, + "location": "local" + }, + "stage": "annotation", + "start_frame": 0, + "state": "new", + "status": "annotation", + "stop_frame": 4, + "target_storage": { + "cloud_storage_id": null, + "id": 52, + "location": "local" + }, + "task_id": 30, + "type": "consensus", + "updated_date": "2025-01-14T17:19:51.388000Z", + "url": "http://localhost:8080/api/jobs/44" + }, + { + "assignee": null, + "assignee_updated_date": null, + "bug_tracker": null, + "consensus_replicas": 0, + "created_date": "2025-01-14T17:19:04.077000Z", + "data_chunk_size": 72, + "data_compressed_chunk_type": "imageset", + "data_original_chunk_type": "imageset", + "dimension": "2d", + "frame_count": 5, + "guide_id": null, + "id": 43, + "issues": { + "count": 0, + "url": "http://localhost:8080/api/issues?job_id=43" + }, + "labels": { + "url": "http://localhost:8080/api/labels?job_id=43" + }, + "mode": "annotation", + "organization": 2, + "parent_job_id": 42, + "project_id": null, + "source_storage": { + "cloud_storage_id": null, + "id": 51, + "location": "local" + }, + "stage": "annotation", + "start_frame": 0, + "state": "in progress", + "status": "annotation", + "stop_frame": 4, + "target_storage": { + "cloud_storage_id": null, + "id": 52, + "location": "local" + }, + "task_id": 30, + "type": "consensus", + "updated_date": "2025-01-14T17:26:01.233000Z", + "url": "http://localhost:8080/api/jobs/43" + }, + { + "assignee": null, + "assignee_updated_date": null, + "bug_tracker": null, + "consensus_replicas": 3, + "created_date": "2025-01-14T17:19:04.062000Z", + "data_chunk_size": 72, + "data_compressed_chunk_type": "imageset", + "data_original_chunk_type": "imageset", + "dimension": "2d", + "frame_count": 5, + "guide_id": null, + "id": 42, + "issues": { + "count": 0, + "url": "http://localhost:8080/api/issues?job_id=42" + }, + "labels": { + "url": "http://localhost:8080/api/labels?job_id=42" + }, + "mode": "annotation", + "organization": 2, + "parent_job_id": null, + "project_id": null, + "source_storage": { + "cloud_storage_id": null, + "id": 51, + "location": "local" + }, + "stage": "annotation", + "start_frame": 0, + "state": "in progress", + "status": "annotation", + "stop_frame": 4, + "target_storage": { + "cloud_storage_id": null, + "id": 52, + "location": "local" + }, + "task_id": 30, + "type": "annotation", + "updated_date": "2025-01-14T17:25:16.549000Z", + "url": "http://localhost:8080/api/jobs/42" + }, + { + "assignee": null, + "assignee_updated_date": null, + "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2024-10-01T12:36:21.511000Z", "data_chunk_size": 3, "data_compressed_chunk_type": "imageset", @@ -24,6 +421,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "annotation", @@ -41,6 +439,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2024-10-01T12:36:21.491000Z", "data_chunk_size": 3, "data_compressed_chunk_type": "imageset", @@ -58,6 +457,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "annotation", @@ -75,6 +475,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2024-10-01T12:36:21.484000Z", "data_chunk_size": 3, "data_compressed_chunk_type": "imageset", @@ -92,6 +493,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "annotation", @@ -109,6 +511,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2024-10-01T12:36:21.476000Z", "data_chunk_size": 3, "data_compressed_chunk_type": "imageset", @@ -126,6 +529,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "annotation", @@ -143,6 +547,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2024-09-23T21:42:23.045000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -160,6 +565,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": 10, "source_storage": { "cloud_storage_id": null, @@ -185,6 +591,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2024-09-23T10:52:04.507000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -202,6 +609,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": null, "source_storage": { "cloud_storage_id": null, @@ -227,6 +635,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2024-09-23T10:51:32.932000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -244,6 +653,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": null, "source_storage": { "cloud_storage_id": null, @@ -269,6 +679,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2024-07-15T15:34:53.594000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -286,6 +697,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": 14, "source_storage": { "cloud_storage_id": null, @@ -311,6 +723,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2024-07-15T15:33:10.549000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -328,6 +741,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": 14, "source_storage": { "cloud_storage_id": null, @@ -353,6 +767,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2024-03-21T20:50:05.838000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -370,6 +785,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "acceptance", @@ -387,6 +803,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2024-03-21T20:50:05.815000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -404,6 +821,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "acceptance", @@ -421,6 +839,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2024-03-21T20:50:05.811000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -438,6 +857,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "acceptance", @@ -455,6 +875,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2024-03-21T20:50:05.805000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -472,6 +893,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "acceptance", @@ -489,6 +911,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2023-05-26T16:11:23.946000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -506,6 +929,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "acceptance", @@ -523,6 +947,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2023-05-26T16:11:23.880000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -540,6 +965,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "annotation", @@ -557,6 +983,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2023-03-27T19:08:07.649000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -574,6 +1001,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": null, "source_storage": { "cloud_storage_id": null, @@ -599,6 +1027,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2023-03-27T19:08:07.649000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -616,6 +1045,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": null, "source_storage": { "cloud_storage_id": null, @@ -641,6 +1071,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2023-03-10T11:57:31.614000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -658,6 +1089,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": 12, "source_storage": { "cloud_storage_id": null, @@ -683,6 +1115,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2023-03-10T11:56:33.757000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -700,6 +1133,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": null, "source_storage": { "cloud_storage_id": null, @@ -725,6 +1159,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2023-03-01T15:36:26.668000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -742,6 +1177,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": 11, "source_storage": { "cloud_storage_id": null, @@ -767,6 +1203,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2023-02-10T14:05:25.947000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -784,6 +1221,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "annotation", @@ -801,6 +1239,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2022-12-01T12:53:10.425000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -818,6 +1257,7 @@ }, "mode": "interpolation", "organization": null, + "parent_job_id": null, "project_id": 8, "source_storage": { "cloud_storage_id": null, @@ -843,6 +1283,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "https://bugtracker.localhost/task/12345", + "consensus_replicas": 0, "created_date": "2022-09-22T14:22:25.820000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -860,6 +1301,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": 5, "source_storage": { "cloud_storage_id": null, @@ -885,6 +1327,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2022-06-08T08:33:06.505000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -902,6 +1345,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": 4, "source_storage": null, "stage": "annotation", @@ -925,6 +1369,7 @@ }, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2022-03-05T10:32:19.149000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -942,6 +1387,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": 2, "source_storage": { "cloud_storage_id": 3, @@ -967,6 +1413,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2022-03-05T09:33:10.420000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -984,6 +1431,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": 1, "source_storage": null, "stage": "annotation", @@ -1001,6 +1449,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2022-03-05T09:33:10.420000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -1018,6 +1467,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": 1, "source_storage": null, "stage": "acceptance", @@ -1035,6 +1485,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2022-03-05T09:33:10.420000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -1052,6 +1503,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": 1, "source_storage": null, "stage": "validation", @@ -1075,6 +1527,7 @@ }, "assignee_updated_date": null, "bug_tracker": "", + "consensus_replicas": 0, "created_date": "2022-03-05T09:33:10.420000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -1092,6 +1545,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": 1, "source_storage": null, "stage": "annotation", @@ -1115,6 +1569,7 @@ }, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2022-03-05T08:30:48.612000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -1132,6 +1587,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "annotation", @@ -1149,6 +1605,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2022-02-21T10:31:52.429000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -1166,6 +1623,7 @@ }, "mode": "annotation", "organization": 2, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "annotation", @@ -1183,6 +1641,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2022-02-16T06:26:54.631000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -1200,6 +1659,7 @@ }, "mode": "annotation", "organization": null, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "annotation", @@ -1223,6 +1683,7 @@ }, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2022-02-16T06:25:48.168000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -1240,6 +1701,7 @@ }, "mode": "interpolation", "organization": null, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "annotation", @@ -1263,6 +1725,7 @@ }, "assignee_updated_date": null, "bug_tracker": null, + "consensus_replicas": 0, "created_date": "2021-12-14T18:50:29.458000Z", "data_chunk_size": 72, "data_compressed_chunk_type": "imageset", @@ -1280,6 +1743,7 @@ }, "mode": "annotation", "organization": 1, + "parent_job_id": null, "project_id": null, "source_storage": null, "stage": "annotation", diff --git a/tests/python/shared/assets/labels.json b/tests/python/shared/assets/labels.json index 529b42a80743..91563de0375e 100644 --- a/tests/python/shared/assets/labels.json +++ b/tests/python/shared/assets/labels.json @@ -1,5 +1,5 @@ { - "count": 44, + "count": 46, "next": null, "previous": null, "results": [ @@ -872,6 +872,50 @@ "sublabels": [], "task_id": 29, "type": "any" + }, + { + "attributes": [ + { + "default_value": "", + "id": 16, + "input_type": "text", + "mutable": false, + "name": "src", + "values": [ + "" + ] + } + ], + "color": "#6080c0", + "has_parent": false, + "id": 78, + "name": "cat", + "parent_id": null, + "sublabels": [], + "task_id": 30, + "type": "any" + }, + { + "attributes": [ + { + "default_value": "", + "id": 17, + "input_type": "text", + "mutable": false, + "name": "src", + "values": [ + "" + ] + } + ], + "color": "#406040", + "has_parent": false, + "id": 79, + "name": "dog", + "parent_id": null, + "sublabels": [], + "task_id": 30, + "type": "any" } ] } \ No newline at end of file diff --git a/tests/python/shared/assets/quality_settings.json b/tests/python/shared/assets/quality_settings.json index dc56352fc1ef..1594c48ff8d9 100644 --- a/tests/python/shared/assets/quality_settings.json +++ b/tests/python/shared/assets/quality_settings.json @@ -1,5 +1,5 @@ { - "count": 24, + "count": 25, "next": null, "previous": null, "results": [ @@ -8,13 +8,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 1, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -29,13 +29,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 2, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -50,13 +50,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 3, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -71,13 +71,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 4, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -92,13 +92,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 5, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -113,13 +113,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 6, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -134,13 +134,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 7, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -155,13 +155,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 8, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -176,13 +176,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 9, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -197,13 +197,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 10, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -218,13 +218,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 11, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -239,13 +239,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 12, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -260,13 +260,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 13, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -281,13 +281,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 14, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -302,13 +302,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 15, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -323,13 +323,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 16, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -344,13 +344,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 17, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -365,13 +365,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 18, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -386,13 +386,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 19, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -407,13 +407,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 20, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -428,13 +428,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 21, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -449,13 +449,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 22, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -470,13 +470,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 23, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -491,13 +491,13 @@ "compare_attributes": true, "compare_groups": true, "compare_line_orientation": true, + "empty_is_annotated": false, "group_match_threshold": 0.5, "id": 24, "iou_threshold": 0.4, "line_orientation_threshold": 0.1, "line_thickness": 0.01, "low_overlap_threshold": 0.8, - "empty_is_annotated": false, "max_validations_per_job": 0, "object_visibility_threshold": 0.05, "oks_sigma": 0.09, @@ -506,6 +506,27 @@ "target_metric": "accuracy", "target_metric_threshold": 0.7, "task_id": 29 + }, + { + "check_covered_annotations": true, + "compare_attributes": true, + "compare_groups": true, + "compare_line_orientation": true, + "empty_is_annotated": false, + "group_match_threshold": 0.5, + "id": 25, + "iou_threshold": 0.4, + "line_orientation_threshold": 0.1, + "line_thickness": 0.01, + "low_overlap_threshold": 0.8, + "max_validations_per_job": 0, + "object_visibility_threshold": 0.05, + "oks_sigma": 0.09, + "panoptic_comparison": true, + "point_size_base": "group_bbox_size", + "target_metric": "accuracy", + "target_metric_threshold": 0.7, + "task_id": 30 } ] } \ No newline at end of file diff --git a/tests/python/shared/assets/tasks.json b/tests/python/shared/assets/tasks.json index cf2d63da785c..500439f5b709 100644 --- a/tests/python/shared/assets/tasks.json +++ b/tests/python/shared/assets/tasks.json @@ -1,5 +1,5 @@ { - "count": 24, + "count": 25, "next": null, "previous": null, "results": [ @@ -7,6 +7,60 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": true, + "created_date": "2025-01-14T17:19:03.665000Z", + "data": 29, + "data_chunk_size": 72, + "data_compressed_chunk_type": "imageset", + "data_original_chunk_type": "imageset", + "dimension": "2d", + "guide_id": null, + "id": 30, + "image_quality": 70, + "jobs": { + "completed": 0, + "count": 9, + "url": "http://localhost:8080/api/jobs?task_id=30", + "validation": 0 + }, + "labels": { + "url": "http://localhost:8080/api/labels?task_id=30" + }, + "mode": "annotation", + "name": "task with consensus and honeypots", + "organization": 2, + "overlap": 0, + "owner": { + "first_name": "User", + "id": 3, + "last_name": "Second", + "url": "http://localhost:8080/api/users/3", + "username": "user2" + }, + "project_id": null, + "segment_size": 0, + "size": 10, + "source_storage": { + "cloud_storage_id": null, + "id": 51, + "location": "local" + }, + "status": "annotation", + "subset": "", + "target_storage": { + "cloud_storage_id": null, + "id": 52, + "location": "local" + }, + "updated_date": "2025-01-14T17:26:01.139000Z", + "url": "http://localhost:8080/api/tasks/30", + "validation_mode": "gt_pool" + }, + { + "assignee": null, + "assignee_updated_date": null, + "bug_tracker": "", + "consensus_enabled": false, "created_date": "2024-10-01T12:36:21.364000Z", "data": 28, "data_chunk_size": 3, @@ -51,6 +105,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2024-09-23T21:42:22.676000Z", "data": 27, "data_chunk_size": 72, @@ -109,6 +164,7 @@ }, "assignee_updated_date": "2024-09-23T10:52:48.769000Z", "bug_tracker": "", + "consensus_enabled": false, "created_date": "2024-09-23T10:52:04.357000Z", "data": 26, "data_chunk_size": 72, @@ -167,6 +223,7 @@ }, "assignee_updated_date": "2024-09-23T10:51:45.525000Z", "bug_tracker": "", + "consensus_enabled": false, "created_date": "2024-09-23T10:51:32.778000Z", "data": 25, "data_chunk_size": 72, @@ -219,6 +276,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2024-07-15T15:34:53.156000Z", "data": 24, "data_chunk_size": 72, @@ -271,6 +329,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2024-07-15T15:33:10.135000Z", "data": 23, "data_chunk_size": 72, @@ -323,6 +382,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2024-03-21T20:50:05.694000Z", "data": 22, "data_chunk_size": 72, @@ -367,6 +427,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2023-05-26T16:11:23.540000Z", "data": 21, "data_chunk_size": 72, @@ -411,6 +472,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2023-03-27T19:08:07.649000Z", "data": 20, "data_chunk_size": 72, @@ -463,6 +525,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2023-03-10T11:57:31.614000Z", "data": 19, "data_chunk_size": 72, @@ -515,6 +578,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2023-03-10T11:56:33.757000Z", "data": 18, "data_chunk_size": 72, @@ -567,6 +631,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2023-03-01T15:36:26.668000Z", "data": 17, "data_chunk_size": 72, @@ -625,6 +690,7 @@ }, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2023-02-10T14:05:25.947000Z", "data": 16, "data_chunk_size": 72, @@ -669,6 +735,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2022-12-01T12:53:10.425000Z", "data": 14, "data_chunk_size": 72, @@ -721,6 +788,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "https://bugtracker.localhost/task/12345", + "consensus_enabled": false, "created_date": "2022-09-22T14:22:25.820000Z", "data": 13, "data_chunk_size": 72, @@ -779,6 +847,7 @@ }, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2022-06-08T08:33:06.505000Z", "data": 12, "data_chunk_size": 72, @@ -823,6 +892,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2022-03-14T13:24:05.852000Z", "dimension": "2d", "guide_id": null, @@ -867,6 +937,7 @@ }, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2022-03-05T10:32:19.149000Z", "data": 11, "data_chunk_size": 72, @@ -925,6 +996,7 @@ }, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2022-03-05T09:33:10.420000Z", "data": 9, "data_chunk_size": 72, @@ -975,6 +1047,7 @@ }, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2022-03-05T08:30:48.612000Z", "data": 8, "data_chunk_size": 72, @@ -1025,6 +1098,7 @@ }, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2022-02-21T10:31:52.429000Z", "data": 7, "data_chunk_size": 72, @@ -1069,6 +1143,7 @@ "assignee": null, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2022-02-16T06:26:54.631000Z", "data": 6, "data_chunk_size": 72, @@ -1119,6 +1194,7 @@ }, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2022-02-16T06:25:48.168000Z", "data": 5, "data_chunk_size": 72, @@ -1169,6 +1245,7 @@ }, "assignee_updated_date": null, "bug_tracker": "", + "consensus_enabled": false, "created_date": "2021-12-14T18:50:29.458000Z", "data": 2, "data_chunk_size": 72, From 35b7ef19474eba69aafe072bb1345f7742ab5a36 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Tue, 14 Jan 2025 19:35:49 +0200 Subject: [PATCH 17/21] Add backup tests --- tests/python/rest_api/test_tasks.py | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index 445b893d9c6d..ca501b758a6a 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -4036,9 +4036,7 @@ def test_can_export_backup_with_both_api_versions( else: assert backup is None - @pytest.mark.parametrize("mode", ["annotation", "interpolation"]) - def test_can_export_backup(self, tasks, mode): - task_id = next(t for t in tasks if t["mode"] == mode and not t["validation_mode"])["id"] + def _test_can_export_backup(self, task_id: int): task = self.client.tasks.retrieve(task_id) filename = self.tmp_dir / f"task_{task.id}_backup.zip" @@ -4047,24 +4045,24 @@ def test_can_export_backup(self, tasks, mode): assert filename.is_file() assert filename.stat().st_size > 0 - def test_can_export_backup_for_honeypot_task(self, tasks): - task_id = next(t for t in tasks if t["validation_mode"] == "gt_pool")["id"] - task = self.client.tasks.retrieve(task_id) + @pytest.mark.parametrize("mode", ["annotation", "interpolation"]) + def test_can_export_backup(self, tasks, mode): + task_id = next(t for t in tasks if t["mode"] == mode and not t["validation_mode"])["id"] + self._test_can_export_backup(task_id) - filename = self.tmp_dir / f"task_{task.id}_backup.zip" - task.download_backup(filename) + def test_can_export_backup_for_consensus_task(self, tasks): + task_id = next(t for t in tasks if t["consensus_enabled"])["id"] + self._test_can_export_backup(task_id) - assert filename.is_file() - assert filename.stat().st_size > 0 + def test_can_export_backup_for_honeypot_task(self, tasks): + task_id = next(t for t in tasks if t["validation_mode"] == "gt_pool")["id"] + self._test_can_export_backup(task_id) def test_cannot_export_backup_for_task_without_data(self, tasks): task_id = next(t for t in tasks if t["jobs"]["count"] == 0)["id"] - task = self.client.tasks.retrieve(task_id) - - filename = self.tmp_dir / f"task_{task.id}_backup.zip" with pytest.raises(ApiException) as exc: - task.download_backup(filename) + self._test_can_export_backup(task_id) assert exc.status == HTTPStatus.BAD_REQUEST assert "Backup of a task without data is not allowed" == exc.body.encode() @@ -4078,6 +4076,10 @@ def test_can_import_backup_with_honeypot_task(self, tasks): task_id = next(t for t in tasks if t["validation_mode"] == "gt_pool")["id"] self._test_can_restore_task_from_backup(task_id) + def test_can_import_backup_with_consensus_task(self, tasks): + task_id = next(t for t in tasks if t["consensus_enabled"])["id"] + self._test_can_restore_task_from_backup(task_id) + @pytest.mark.parametrize("mode", ["annotation", "interpolation"]) def test_can_import_backup_for_task_in_nondefault_state(self, tasks, mode): # Reproduces the problem with empty 'mode' in a restored task, From 4c41c2a5f2a45996208f6200107bf78ba345eeca Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Wed, 15 Jan 2025 12:44:04 +0200 Subject: [PATCH 18/21] Fix python 3.9 compatibility --- tests/python/rest_api/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/rest_api/utils.py b/tests/python/rest_api/utils.py index edf39430885d..3583adc540b6 100644 --- a/tests/python/rest_api/utils.py +++ b/tests/python/rest_api/utils.py @@ -611,6 +611,6 @@ def calc_end_frame(start_frame: int, stop_frame: int, frame_step: int) -> int: def unique( - it: Iterator[_T] | Iterable[_T], *, key: Callable[[_T], Hashable] = None + it: Union[Iterator[_T], Iterable[_T]], *, key: Callable[[_T], Hashable] = None ) -> Iterable[_T]: return {key(v): v for v in it}.values() From 287901044df68e50b5c7b54b38e85562abb0fc9e Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Wed, 15 Jan 2025 13:17:35 +0200 Subject: [PATCH 19/21] Add annotations test for consensus task --- tests/python/rest_api/test_tasks.py | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index ca501b758a6a..d81edaae9d79 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -4881,6 +4881,44 @@ def test_can_change_honeypot_frames_in_annotation_jobs( assert new_job_meta.chunks_updated_date > old_job_meta.chunks_updated_date +@pytest.mark.usefixtures("restore_db_per_function") +class TestWorkWithConsensusTasks: + @pytest.mark.parametrize("task_id", [30]) + def test_replica_annotations_are_not_present_in_task_annotations( + self, admin_user, jobs, annotations, task_id: int + ): + task_jobs = [j for j in jobs if j["task_id"] == task_id] + consensus_jobs = [j for j in task_jobs if j["type"] == "consensus"] + + # Ensure there are annotations in replicas + assert any( + len(annotations["job"][str(j["id"])]["tags"]) + + len(annotations["job"][str(j["id"])]["shapes"]) + + len(annotations["job"][str(j["id"])]["tracks"]) + for j in consensus_jobs + ) + + with make_api_client(admin_user) as api_client: + for annotation_job in task_jobs: + if annotation_job["type"] != "consensus": + api_client.jobs_api.destroy_annotations(annotation_job["id"]) + + updated_task_annotations, _ = api_client.tasks_api.retrieve_annotations(task_id) + assert not updated_task_annotations.tags + assert not updated_task_annotations.shapes + assert not updated_task_annotations.tracks + + for consensus_job in consensus_jobs: + job_annotations = annotations["job"][str(consensus_job["id"])] + updated_job_annotations, _ = api_client.jobs_api.retrieve_annotations( + consensus_job["id"] + ) + + assert len(job_annotations["tags"]) == len(updated_job_annotations.tags) + assert len(job_annotations["shapes"]) == len(updated_job_annotations.shapes) + assert len(job_annotations["tracks"]) == len(updated_job_annotations.tracks) + + @pytest.mark.usefixtures("restore_db_per_class") class TestGetTaskPreview: def _test_task_preview_200(self, username, task_id, **kwargs): From f3de175e965f60821ff5b287a049b3e81fbd46dd Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Wed, 15 Jan 2025 13:33:40 +0200 Subject: [PATCH 20/21] Fix project backup import --- cvat/apps/engine/backup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cvat/apps/engine/backup.py b/cvat/apps/engine/backup.py index 41fadf4855ec..d81e1ab085e8 100644 --- a/cvat/apps/engine/backup.py +++ b/cvat/apps/engine/backup.py @@ -956,7 +956,6 @@ class _ProjectBackupBase(_BackupBase): def _prepare_project_meta(self, project): allowed_fields = { 'bug_tracker', - 'dimension', 'labels', 'name', 'status', From f92a3c31446c4adf933eff350248ce2150b708f8 Mon Sep 17 00:00:00 2001 From: Maxim Zhiltsov Date: Wed, 15 Jan 2025 16:46:00 +0200 Subject: [PATCH 21/21] Fix honeypot task selection --- tests/python/rest_api/test_tasks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/python/rest_api/test_tasks.py b/tests/python/rest_api/test_tasks.py index d81edaae9d79..a98d5272a24f 100644 --- a/tests/python/rest_api/test_tasks.py +++ b/tests/python/rest_api/test_tasks.py @@ -4336,6 +4336,7 @@ def fxt_task_with_honeypots( j for j in jobs if j["type"] == "ground_truth" + if j["frame_count"] >= 4 if job_has_annotations(j["id"]) if tasks[j["task_id"]]["validation_mode"] == "gt_pool" if tasks[j["task_id"]]["size"]