From 7c106b35442194ecf696f1d3cb8637de7fc5f48a Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Wed, 17 Oct 2018 11:14:05 +0200 Subject: [PATCH 01/10] feat(native): Introduce CFI Caches --- src/sentry/models/debugfile.py | 241 ++++++++++++++++++++-------- src/sentry/tasks/assemble.py | 18 +-- src/sentry/tasks/symcache_update.py | 2 +- 3 files changed, 180 insertions(+), 81 deletions(-) diff --git a/src/sentry/models/debugfile.py b/src/sentry/models/debugfile.py index 36449a648b9458..d780d01a22f0d4 100644 --- a/src/sentry/models/debugfile.py +++ b/src/sentry/models/debugfile.py @@ -28,17 +28,18 @@ from symbolic import FatObject, SymbolicError, ObjectErrorUnsupportedObject, \ SYMCACHE_LATEST_VERSION, SymCache, SymCacheErrorMissingDebugInfo, \ - SymCacheErrorMissingDebugSection + SymCacheErrorMissingDebugSection, CfiCache, CfiErrorMissingDebugInfo from sentry import options from sentry.cache import default_cache +from sentry.constants import KNOWN_DIF_TYPES from sentry.db.models import FlexibleForeignKey, Model, \ sane_repr, BaseManager, BoundedPositiveIntegerField from sentry.models.file import File, ChunkFileState -from sentry.utils.zip import safe_extract_zip -from sentry.constants import KNOWN_DIF_TYPES from sentry.reprocessing import resolve_processing_issue, \ bump_reprocessing_revision +from sentry.utils.zip import safe_extract_zip +from sentry.utils.decorators import classproperty logger = logging.getLogger(__name__) @@ -293,15 +294,13 @@ def file_extension(self): return '' @property - def features(self): - return frozenset((self.data or {}).get('features', [])) + def supports_caches(self): + return ProjectSymCacheFile.computes_from(self) \ + or ProjectCfiCacheFile.computes_from(self) @property - def supports_symcache(self): - if self.data is None: - return self.dif_type in ('breakpad', 'macho', 'elf') - else: - return 'debug' in self.features + def features(self): + return frozenset((self.data or {}).get('features', [])) def delete(self, *args, **kwargs): symcache = self.projectsymcachefile.select_related('cache_file').first() @@ -312,27 +311,110 @@ def delete(self, *args, **kwargs): self.file.delete() -class ProjectSymCacheFile(Model): +class ProjectCacheFile(Model): + """Abstract base class for all debug cache files.""" __core__ = False project = FlexibleForeignKey('sentry.Project', null=True) cache_file = FlexibleForeignKey('sentry.File') - dsym_file = FlexibleForeignKey('sentry.ProjectDebugFile', rel_class=OneToOneRel) + debug_file = FlexibleForeignKey( + 'sentry.ProjectDebugFile', + rel_class=OneToOneRel, + db_column='dsym_file_id') checksum = models.CharField(max_length=40) version = BoundedPositiveIntegerField() + __repr__ = sane_repr('debug_file__debug_id', 'version') + class Meta: - unique_together = (('project', 'dsym_file'),) - db_table = 'sentry_projectsymcachefile' + abstract = True + unique_together = (('project', 'debug_file'),) app_label = 'sentry' - __repr__ = sane_repr('debug_id') + @classproperty + def ignored_errors(cls): + """Returns a set of errors that can safely be ignored during conversion. + These errors should be expected by bad input data and do not indicate a + programming error. + """ + raise NotImplementedError + + @classproperty + def required_features(cls): + """Returns a set of features object files must have in order to support + generating this cache. + """ + raise NotImplementedError + + @classproperty + def cache_cls(cls): + """Returns the class of the raw cache referenced by this model. It can + be used to load caches from the file system or to convert object files. + """ + raise NotImplementedError + + @classproperty + def cache_name(cls): + """Returns the name of the cache class in lower case. Can be used for + file extensions, cache keys, logs, etc. + """ + return cls.cache_cls.__name__.lower() + + @classmethod + def computes_from(cls, debug_file): + """Indicates whether the cache can be computed from the given DIF.""" + return set(cls.required_features) <= debug_file.features def delete(self, *args, **kwargs): - super(ProjectSymCacheFile, self).delete(*args, **kwargs) + super(ProjectCacheFile, self).delete(*args, **kwargs) self.cache_file.delete() +class ProjectSymCacheFile(ProjectCacheFile): + """Cache for native address symbolication: SymCache.""" + + class Meta(ProjectCacheFile.Meta): + db_table = 'sentry_projectsymcachefile' + + @classproperty + def ignored_errors(cls): + return (SymCacheErrorMissingDebugSection, SymCacheErrorMissingDebugInfo) + + @classproperty + def required_features(cls): + return ('debug',) + + @classproperty + def cache_cls(cls): + return SymCache + + @classmethod + def computes_from(cls, debug_file): + if debug_file.data is None: + # Compatibility with legacy DIFs before features were introduced + return debug_file.dif_type in ('breakpad', 'macho', 'elf') + return super(ProjectSymCacheFile, cls).computes_from(debug_file) + + +class ProjectCfiCacheFile(ProjectCacheFile): + """Cache for stack unwinding information: CfiCache.""" + + class Meta(ProjectCacheFile.Meta): + db_table = 'sentry_projectcficachefile' + + @classproperty + def ignored_errors(cls): + return (CfiErrorMissingDebugInfo,) + + @classproperty + def required_features(cls): + return ('unwind',) + + @classproperty + def cache_cls(cls): + return CfiCache + + def clean_redundant_difs(project, debug_id): """Deletes redundant debug files from the database and file storage. A debug file is considered redundant if there is a newer file with the same debug @@ -358,7 +440,7 @@ def create_dif_from_id(project, dif_type, cpu_name, debug_id, data, basename, fileobj=None, file=None): """This creates a mach dsym file or proguard mapping from the given debug id and open file object to a debug file. This will not verify the - debug id (intentionally so). Use `create_files_from_dif_zip` for doing + debug id(intentionally so). Use `create_files_from_dif_zip` for doing everything. """ if dif_type == 'proguard': @@ -443,7 +525,7 @@ def _analyze_progard_filename(filename): def detect_dif_from_path(path): - """This detects which kind of dif (Debug Information File) the path + """This detects which kind of dif(Debug Information File) the path provided is. It returns an array since a FatObject can contain more than on dif. """ @@ -480,7 +562,7 @@ def detect_dif_from_path(path): def create_debug_file_from_dif(to_create, project, overwrite_filename=None): - """Create a ProjectDebugFile from a dif (Debug Information File) and + """Create a ProjectDebugFile from a dif(Debug Information File) and return an array of created objects. """ rv = [] @@ -498,7 +580,7 @@ def create_debug_file_from_dif(to_create, project, overwrite_filename=None): return rv -def create_files_from_dif_zip(fileobj, project, update_symcaches=True): +def create_files_from_dif_zip(fileobj, project, update_caches=True): """Creates all missing debug files from the given zip file. This returns a list of all files created. """ @@ -521,12 +603,12 @@ def create_files_from_dif_zip(fileobj, project, update_symcaches=True): rv = create_debug_file_from_dif(to_create, project) - # By default we trigger the symcache generation on upload to avoid - # some obvious dogpiling. - if update_symcaches: + # Trigger generation of symcaches and cficaches to avoid dogpiling when + # events start coming in. + if update_caches: from sentry.tasks.symcache_update import symcache_update ids_to_update = [six.text_type(dif.debug_id) for dif in rv - if dif.supports_symcache] + if dif.supports_caches] if ids_to_update: symcache_update.delay(project_id=project.id, debug_ids=ids_to_update) @@ -547,43 +629,55 @@ def cache_path(self): def get_project_path(self, project): return os.path.join(self.cache_path, six.text_type(project.id)) - def update_symcaches(self, project, debug_ids): - """Given some debug ids of DIFs this will update the symcaches for - all of these if a symcache is supported for that symbol. + def update_caches(self, project, debug_ids): + """Updates symcaches and cficaches for all debug files matching the + given debug ids, if the respective files support any of those caches. """ - self._get_symcaches_impl(project, debug_ids) + # XXX: Worst case, this might download the same DIF twice. + self._get_caches_impl(project, debug_ids, ProjectSymCacheFile) + self._get_caches_impl(project, debug_ids, ProjectCfiCacheFile) def get_symcaches(self, project, debug_ids, on_dif_referenced=None, with_conversion_errors=False): """Given some debug ids returns the symcaches loaded for these debug ids. """ - cachefiles, conversion_errors = self._get_symcaches_impl( - project, debug_ids, on_dif_referenced) - symcaches = self._load_cachefiles_via_fs(project, cachefiles) + cachefiles, conversion_errors = self._get_caches_impl( + project, debug_ids, ProjectSymCacheFile, on_dif_referenced) + symcaches = self._load_cachefiles_via_fs(project, cachefiles, SymCache) if with_conversion_errors: return symcaches, dict((k, v) for k, v in conversion_errors.items()) return symcaches - def generate_symcache(self, project, debug_file, fileobj=None): - """Generate a single symcache for a debug id based on the passed file - contents. If the tempfile is not passed then its opened again. + def generate_caches(self, project, dif, fileobj=None): + """Generates a SymCache and CfiCache for the given debug information + file if it supports these formats. Otherwise, a no - op. The caches are + computed sequentially. + The first error to occur is returned, otherwise None. """ - if not debug_file.supports_symcache: - raise RuntimeError('This file type does not support symcaches') + if not dif.supports_caches: + return None if fileobj is None: - fileobj = debug_file.file.getfile(as_tempfile=True) + fileobj = dif.file.getfile(as_tempfile=True) close_fileobj = True else: fileobj.seek(0) close_fileobj = False try: - return self._update_cachefile(debug_file, fileobj) + _, _, error = self._update_cachefile(dif, fileobj, ProjectSymCacheFile) + if error is not None: + return error + + _, _, error = self._update_cachefile(dif, fileobj, ProjectCfiCacheFile) + if error is not None: + return error finally: if close_fileobj: fileobj.close() + return None + def fetch_difs(self, project, debug_ids, features=None): """Given some ids returns an id to path mapping for where the debug symbol files are on the FS. @@ -604,11 +698,11 @@ def fetch_difs(self, project, debug_ids, features=None): return rv - def _get_symcaches_impl(self, project, debug_ids, on_dif_referenced=None): + def _get_caches_impl(self, project, debug_ids, cls, on_dif_referenced=None): # Fetch debug files first and invoke the callback if we need debug_ids = [six.text_type(debug_id).lower() for debug_id in debug_ids] debug_files = ProjectDebugFile.objects.find_by_debug_ids( - project, debug_ids, features=['debug']) + project, debug_ids, features=cls.required_features) # Notify the caller that we have used a symbol file if on_dif_referenced is not None: @@ -617,9 +711,9 @@ def _get_symcaches_impl(self, project, debug_ids, on_dif_referenced=None): # Now find all the cache files we already have found_ids = [d.id for d in six.itervalues(debug_files)] - existing_caches = ProjectSymCacheFile.objects \ - .filter(project=project, dsym_file_id__in=found_ids) \ - .select_related('cache_file', 'dsym_file__debug_id') + existing_caches = cls.objects \ + .filter(project=project, debug_file_id__in=found_ids) \ + .select_related('cache_file', 'debug_file__debug_id') # Check for missing and out-of-date cache files. Outdated files are # removed to be re-created immediately. @@ -627,7 +721,7 @@ def _get_symcaches_impl(self, project, debug_ids, on_dif_referenced=None): to_update = debug_files.copy() for cache_file in existing_caches: if cache_file.version == SYMCACHE_LATEST_VERSION: - debug_id = cache_file.dsym_file.debug_id + debug_id = cache_file.debug_file.debug_id to_update.pop(debug_id, None) caches.append((debug_id, cache_file, None)) else: @@ -643,7 +737,7 @@ def _get_symcaches_impl(self, project, debug_ids, on_dif_referenced=None): return caches, conversion_errors - def _update_cachefiles(self, project, debug_files): + def _update_cachefiles(self, project, debug_files, cls): rv = [] conversion_errors = {} @@ -660,10 +754,10 @@ def _update_cachefiles(self, project, debug_files): continue # Download the original debug symbol and convert the object file to - # a symcache. This can either yield a symcache, an error or none of + # a cache. This can either yield a cache object, an error or none of # the above. THE FILE DOWNLOAD CAN TAKE SIGNIFICANT TIME. with debug_file.file.getfile(as_tempfile=True) as tf: - file, cache, err = self._update_cachefile(debug_file, tf) + file, cache, err = self._update_cachefile(debug_file, tf, cls) # Store this conversion error so that we can skip subsequent # conversions. There might be concurrent conversions running for the @@ -678,9 +772,13 @@ def _update_cachefiles(self, project, debug_files): return rv, conversion_errors - def _update_cachefile(self, debug_file, fileobj): + def _update_cachefile(self, debug_file, fileobj, cls): debug_id = debug_file.debug_id + # Skip silently if this cache cannot be computed from the given DIF + if not cls.computes_from(debug_file): + return None, None, None + # Locate the object inside the FatObject. Since we have keyed debug # files by debug_id, we expect a corresponding object. Otherwise, we # fail silently, just like with missing symbols. @@ -689,39 +787,40 @@ def _update_cachefile(self, debug_file, fileobj): o = fo.get_object(id=debug_id) if o is None: return None, None, None - symcache = o.make_symcache() + + cache = cls.cache_cls.from_object(o) except SymbolicError as e: - if not isinstance(e, (SymCacheErrorMissingDebugSection, SymCacheErrorMissingDebugInfo)): - logger.error('dsymfile.symcache-build-error', + if not isinstance(e, cls.ignored_errors): + logger.error('dsymfile.%s-build-error' % cls.cache_name, exc_info=True, extra=dict(debug_id=debug_id)) return None, None, e.message - file = File.objects.create(name=debug_id, type='project.symcache') - file.putfile(symcache.open_stream()) + file = File.objects.create(name=debug_id, type='project.%s' % cls.cache_name) + file.putfile(cache.open_stream()) - # Try to insert the new SymCache into the database. This only fail if + # Try to insert the new Cache into the database. This only fail if # (1) another process has concurrently added the same sym cache, or if # (2) the debug symbol was deleted, either due to a newer upload or via # the API. try: with transaction.atomic(): - return ProjectSymCacheFile.objects.create( + return cls.objects.create( project=debug_file.project, cache_file=file, - dsym_file=debug_file, + debug_file=debug_file, checksum=debug_file.file.checksum, - version=symcache.file_format_version, - ), symcache, None + version=cache.file_format_version, + ), cache, None except IntegrityError: file.delete() - # Check for a concurrently inserted symcache and use that instead. This + # Check for a concurrently inserted cache and use that instead. This # could have happened (1) due to a concurrent insert, or (2) a new - # upload that has already succeeded to compute a symcache. The latter + # upload that has already succeeded to compute a cache. The latter # case is extremely unlikely. - cache_file = ProjectSymCacheFile.objects \ - .filter(project=debug_file.project, dsym_file__debug_id=debug_id) \ + cache_file = cls.objects \ + .filter(project=debug_file.project, debug_file__debug_id=debug_id) \ .select_related('cache_file') \ .order_by('-id') \ .first() @@ -729,15 +828,17 @@ def _update_cachefile(self, debug_file, fileobj): if cache_file is not None: return cache_file, None, None - # There was no new symcache, indicating that the debug file has been + # There was no new cache, indicating that the debug file has been # replaced with a newer version. Another job will create the - # corresponding symcache eventually. To prevent querying the database - # another time, simply use the in-memory symcache for now: - return None, symcache, None + # corresponding cache eventually. To prevent querying the database + # another time, simply use the in-memory cache for now: + return None, cache, None - def _load_cachefiles_via_fs(self, project, cachefiles): + def _load_cachefiles_via_fs(self, project, cachefiles, cls): rv = {} base = self.get_project_path(project) + cls_name = cls.__name__.lower() + for debug_id, model, cache in cachefiles: # If we're given a cache instance, use that over accessing the file # system or potentially even blob storage. @@ -745,12 +846,12 @@ def _load_cachefiles_via_fs(self, project, cachefiles): rv[debug_id] = cache continue elif model is None: - raise RuntimeError('missing symcache file to load from fs') + raise RuntimeError('missing %s file to load from fs' % cls_name) # Try to locate a cached instance from the file system and bump the # timestamp to indicate it is still being used. Otherwise, download # from the blob store and place it in the cache folder. - cachefile_name = '%s_%s.symcache' % (model.id, model.version) + cachefile_name = '%s_%s.%s' % (model.id, model.version, cls_name) cachefile_path = os.path.join(base, cachefile_name) try: stat = os.stat(cachefile_path) @@ -763,7 +864,7 @@ def _load_cachefiles_via_fs(self, project, cachefiles): if stat.st_ctime < now - ONE_DAY: os.utime(cachefile_path, (now, now)) - rv[debug_id] = SymCache.from_path(cachefile_path) + rv[debug_id] = cls.from_path(cachefile_path) return rv def clear_old_entries(self): diff --git a/src/sentry/tasks/assemble.py b/src/sentry/tasks/assemble.py index d7d4ad25a000f6..aa2328b21f0cab 100644 --- a/src/sentry/tasks/assemble.py +++ b/src/sentry/tasks/assemble.py @@ -54,19 +54,17 @@ def assemble_dif(project_id, name, checksum, chunks, **kwargs): indicate_success = True delete_file = False - # Bump the reprocessing revision since the symbol has changed and - # might resolve processing issues. If the file was not created, - # someone else has created it and will bump the revision instead. if created: + # Bump the reprocessing revision since the symbol has changed + # and might resolve processing issues. If the file was not + # created, someone else has created it and will bump the + # revision instead. bump_reprocessing_revision(project) - # If we need to write a symcache we can use the - # `generate_symcache` method to attempt to write one. - # This way we can also capture down the error if we need - # to. - if created and dif.supports_symcache: - _, _, error = ProjectDebugFile.difcache.generate_symcache( - project, dif, temp_file) + # Try to generate caches from this DIF immediately. If this + # fails, we can capture the error and report it to the uploader. + # Also, we remove the file to prevent it from erroring again. + error = ProjectDebugFile.difcache.generate_caches(project, dif, temp_file) if error is not None: set_assemble_status(project, checksum, ChunkFileState.ERROR, detail=error) diff --git a/src/sentry/tasks/symcache_update.py b/src/sentry/tasks/symcache_update.py index fde264df73c6c0..022592a42a5c4e 100644 --- a/src/sentry/tasks/symcache_update.py +++ b/src/sentry/tasks/symcache_update.py @@ -15,4 +15,4 @@ def symcache_update(project_id, debug_ids, **kwargs): except Project.DoesNotExist: return - ProjectDebugFile.difcache.update_symcaches(project, debug_ids) + ProjectDebugFile.difcache.update_caches(project, debug_ids) From 52c1f62e2aae50079ee4204d5e1f254519696542 Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Thu, 18 Oct 2018 14:39:55 +0200 Subject: [PATCH 02/10] db: Create migration for ProjectCfiCacheFile --- ...add_unique_projectcficachefile_project_.py | 1263 +++++++++++++++++ 1 file changed, 1263 insertions(+) create mode 100644 src/sentry/south_migrations/0442_auto__add_projectcficachefile__add_unique_projectcficachefile_project_.py diff --git a/src/sentry/south_migrations/0442_auto__add_projectcficachefile__add_unique_projectcficachefile_project_.py b/src/sentry/south_migrations/0442_auto__add_projectcficachefile__add_unique_projectcficachefile_project_.py new file mode 100644 index 00000000000000..eb26a73741b048 --- /dev/null +++ b/src/sentry/south_migrations/0442_auto__add_projectcficachefile__add_unique_projectcficachefile_project_.py @@ -0,0 +1,1263 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + # Flag to indicate if this migration is too risky + # to run online and needs to be coordinated for offline + is_dangerous = False + + def forwards(self, orm): + # Adding model 'ProjectCfiCacheFile' + db.create_table('sentry_projectcficachefile', ( + ('id', self.gf('sentry.db.models.fields.bounded.BoundedBigAutoField')(primary_key=True)), + ('project', self.gf('sentry.db.models.fields.foreignkey.FlexibleForeignKey')( + to=orm['sentry.Project'], null=True)), + ('cache_file', self.gf('sentry.db.models.fields.foreignkey.FlexibleForeignKey')( + to=orm['sentry.File'])), + ('debug_file', self.gf('sentry.db.models.fields.foreignkey.FlexibleForeignKey')( + to=orm['sentry.ProjectDebugFile'], db_column='dsym_file_id')), + ('checksum', self.gf('django.db.models.fields.CharField')(max_length=40)), + ('version', self.gf('sentry.db.models.fields.bounded.BoundedPositiveIntegerField')()), + )) + db.send_create_signal('sentry', ['ProjectCfiCacheFile']) + + # Adding unique constraint on 'ProjectCfiCacheFile', fields ['project', 'debug_file'] + db.create_unique('sentry_projectcficachefile', ['project_id', 'dsym_file_id']) + + def backwards(self, orm): + # Removing unique constraint on 'ProjectCfiCacheFile', fields ['project', 'debug_file'] + db.delete_unique('sentry_projectcficachefile', ['project_id', 'dsym_file_id']) + + # Deleting model 'ProjectCfiCacheFile' + db.delete_table('sentry_projectcficachefile') + + models = { + 'sentry.activity': { + 'Meta': {'object_name': 'Activity'}, + 'data': ('sentry.db.models.fields.gzippeddict.GzippedDictField', [], {'null': 'True'}), + 'datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Group']", 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ident': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'type': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']", 'null': 'True'}) + }, + 'sentry.apiapplication': { + 'Meta': {'object_name': 'ApiApplication'}, + 'allowed_origins': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'client_id': ('django.db.models.fields.CharField', [], {'default': "'fe7de2b80bb0488e8b46af7449bbb39616fdb271e2ac4decaa6a569623977f0f'", 'unique': 'True', 'max_length': '64'}), + 'client_secret': ('sentry.db.models.fields.encrypted.EncryptedTextField', [], {'default': "'cfd19ab9973e41e591157b9bffdf88f7999d4b1eabf04443a5b8976d0efcbd9e'"}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'homepage_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "'Concise Humpback'", 'max_length': '64', 'blank': 'True'}), + 'owner': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}), + 'privacy_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}), + 'redirect_uris': ('django.db.models.fields.TextField', [], {}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'db_index': 'True'}), + 'terms_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}) + }, + 'sentry.apiauthorization': { + 'Meta': {'unique_together': "(('user', 'application'),)", 'object_name': 'ApiAuthorization'}, + 'application': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.ApiApplication']", 'null': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'scope_list': ('sentry.db.models.fields.array.ArrayField', [], {'of': (u'django.db.models.fields.TextField', [], {})}), + 'scopes': ('django.db.models.fields.BigIntegerField', [], {'default': 'None'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.apigrant': { + 'Meta': {'object_name': 'ApiGrant'}, + 'application': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.ApiApplication']"}), + 'code': ('django.db.models.fields.CharField', [], {'default': "'b577b1a10da142308b5857a728014ce9'", 'max_length': '64', 'db_index': 'True'}), + 'expires_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2018, 10, 18, 0, 0)', 'db_index': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'redirect_uri': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'scope_list': ('sentry.db.models.fields.array.ArrayField', [], {'of': (u'django.db.models.fields.TextField', [], {})}), + 'scopes': ('django.db.models.fields.BigIntegerField', [], {'default': 'None'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.apikey': { + 'Meta': {'object_name': 'ApiKey'}, + 'allowed_origins': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}), + 'label': ('django.db.models.fields.CharField', [], {'default': "'Default'", 'max_length': '64', 'blank': 'True'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'key_set'", 'to': "orm['sentry.Organization']"}), + 'scope_list': ('sentry.db.models.fields.array.ArrayField', [], {'of': (u'django.db.models.fields.TextField', [], {})}), + 'scopes': ('django.db.models.fields.BigIntegerField', [], {'default': 'None'}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'sentry.apitoken': { + 'Meta': {'object_name': 'ApiToken'}, + 'application': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.ApiApplication']", 'null': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'expires_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2018, 11, 17, 0, 0)', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'refresh_token': ('django.db.models.fields.CharField', [], {'default': "'16f086aa70b04baf829f9c8466501d6b6eb67ac3677b457488e0f13b84df634e'", 'max_length': '64', 'unique': 'True', 'null': 'True'}), + 'scope_list': ('sentry.db.models.fields.array.ArrayField', [], {'of': (u'django.db.models.fields.TextField', [], {})}), + 'scopes': ('django.db.models.fields.BigIntegerField', [], {'default': 'None'}), + 'token': ('django.db.models.fields.CharField', [], {'default': "'e8ff761dc58b4b08b7022749ef2605bc62a4d6058afa4f4881cf8e2b2ae20fa7'", 'unique': 'True', 'max_length': '64'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.assistantactivity': { + 'Meta': {'unique_together': "(('user', 'guide_id'),)", 'object_name': 'AssistantActivity', 'db_table': "'sentry_assistant_activity'"}, + 'dismissed_ts': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'guide_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'useful': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}), + 'viewed_ts': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}) + }, + 'sentry.auditlogentry': { + 'Meta': {'object_name': 'AuditLogEntry'}, + 'actor': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'blank': 'True', 'related_name': "'audit_actors'", 'null': 'True', 'to': "orm['sentry.User']"}), + 'actor_key': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.ApiKey']", 'null': 'True', 'blank': 'True'}), + 'actor_label': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'data': ('sentry.db.models.fields.gzippeddict.GzippedDictField', [], {}), + 'datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'event': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}), + 'target_object': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'target_user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'blank': 'True', 'related_name': "'audit_targets'", 'null': 'True', 'to': "orm['sentry.User']"}) + }, + 'sentry.authenticator': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'Authenticator', 'db_table': "'auth_authenticator'"}, + 'config': ('sentry.db.models.fields.encrypted.EncryptedPickledObjectField', [], {}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedAutoField', [], {'primary_key': 'True'}), + 'last_used_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'type': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.authidentity': { + 'Meta': {'unique_together': "(('auth_provider', 'ident'), ('auth_provider', 'user'))", 'object_name': 'AuthIdentity'}, + 'auth_provider': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.AuthProvider']"}), + 'data': ('sentry.db.models.fields.encrypted.EncryptedJsonField', [], {'default': '{}'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ident': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'last_synced': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_verified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.authprovider': { + 'Meta': {'object_name': 'AuthProvider'}, + 'config': ('sentry.db.models.fields.encrypted.EncryptedJsonField', [], {'default': '{}'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_global_access': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'default_role': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '50'}), + 'default_teams': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sentry.Team']", 'symmetrical': 'False', 'blank': 'True'}), + 'flags': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']", 'unique': 'True'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'sync_time': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}) + }, + 'sentry.broadcast': { + 'Meta': {'object_name': 'Broadcast'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_expires': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2018, 10, 25, 0, 0)', 'null': 'True', 'blank': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), + 'link': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'message': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'upstream_id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}) + }, + 'sentry.broadcastseen': { + 'Meta': {'unique_together': "(('broadcast', 'user'),)", 'object_name': 'BroadcastSeen'}, + 'broadcast': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Broadcast']"}), + 'date_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.commit': { + 'Meta': {'unique_together': "(('repository_id', 'key'),)", 'object_name': 'Commit', 'index_together': "(('repository_id', 'date_added'),)"}, + 'author': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.CommitAuthor']", 'null': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'message': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'repository_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}) + }, + 'sentry.commitauthor': { + 'Meta': {'unique_together': "(('organization_id', 'email'), ('organization_id', 'external_id'))", 'object_name': 'CommitAuthor'}, + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'external_id': ('django.db.models.fields.CharField', [], {'max_length': '164', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}) + }, + 'sentry.commitfilechange': { + 'Meta': {'unique_together': "(('commit', 'filename'),)", 'object_name': 'CommitFileChange'}, + 'commit': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Commit']"}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '1'}) + }, + 'sentry.counter': { + 'Meta': {'object_name': 'Counter', 'db_table': "'sentry_projectcounter'"}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']", 'unique': 'True'}), + 'value': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}) + }, + 'sentry.deletedorganization': { + 'Meta': {'object_name': 'DeletedOrganization'}, + 'actor_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'actor_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'actor_label': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'date_deleted': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}) + }, + 'sentry.deletedproject': { + 'Meta': {'object_name': 'DeletedProject'}, + 'actor_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'actor_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'actor_label': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'date_deleted': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'organization_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'organization_slug': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}), + 'platform': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}) + }, + 'sentry.deletedteam': { + 'Meta': {'object_name': 'DeletedTeam'}, + 'actor_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'actor_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'actor_label': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'date_deleted': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'organization_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'organization_slug': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}), + 'reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}) + }, + 'sentry.deploy': { + 'Meta': {'object_name': 'Deploy'}, + 'date_finished': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_started': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'environment_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'notified': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'release': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Release']"}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'sentry.discoversavedquery': { + 'Meta': {'object_name': 'DiscoverSavedQuery'}, + 'created_by': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']", 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}), + 'projects': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sentry.Project']", 'through': "orm['sentry.DiscoverSavedQueryProject']", 'symmetrical': 'False'}), + 'query': ('jsonfield.fields.JSONField', [], {'default': '{}'}) + }, + 'sentry.discoversavedqueryproject': { + 'Meta': {'unique_together': "(('project', 'discover_saved_query'),)", 'object_name': 'DiscoverSavedQueryProject'}, + 'discover_saved_query': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.DiscoverSavedQuery']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}) + }, + 'sentry.distribution': { + 'Meta': {'unique_together': "(('release', 'name'),)", 'object_name': 'Distribution'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'release': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Release']"}) + }, + 'sentry.dsymapp': { + 'Meta': {'unique_together': "(('project', 'platform', 'app_id'),)", 'object_name': 'DSymApp'}, + 'app_id': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'data': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'last_synced': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'platform': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'sync_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}) + }, + 'sentry.email': { + 'Meta': {'object_name': 'Email'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('sentry.db.models.fields.citext.CIEmailField', [], {'unique': 'True', 'max_length': '75'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}) + }, + 'sentry.environment': { + 'Meta': {'unique_together': "(('organization_id', 'name'),)", 'object_name': 'Environment'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'projects': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sentry.Project']", 'through': "orm['sentry.EnvironmentProject']", 'symmetrical': 'False'}) + }, + 'sentry.environmentproject': { + 'Meta': {'unique_together': "(('project', 'environment'),)", 'object_name': 'EnvironmentProject'}, + 'environment': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Environment']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'is_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}) + }, + 'sentry.event': { + 'Meta': {'unique_together': "(('project_id', 'event_id'),)", 'object_name': 'Event', 'db_table': "'sentry_message'", 'index_together': "(('group_id', 'datetime'),)"}, + 'data': ('sentry.db.models.fields.node.NodeField', [], {'null': 'True', 'blank': 'True'}), + 'datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'event_id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'db_column': "'message_id'"}), + 'group_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {}), + 'platform': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'time_spent': ('sentry.db.models.fields.bounded.BoundedIntegerField', [], {'null': 'True'}) + }, + 'sentry.eventattachment': { + 'Meta': {'unique_together': "(('project_id', 'event_id', 'file'),)", 'object_name': 'EventAttachment', 'index_together': "(('project_id', 'date_added'),)"}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'event_id': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.File']"}), + 'group_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.TextField', [], {}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}) + }, + 'sentry.eventmapping': { + 'Meta': {'unique_together': "(('project_id', 'event_id'),)", 'object_name': 'EventMapping'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'event_id': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'group_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}) + }, + 'sentry.eventprocessingissue': { + 'Meta': {'unique_together': "(('raw_event', 'processing_issue'),)", 'object_name': 'EventProcessingIssue'}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'processing_issue': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.ProcessingIssue']"}), + 'raw_event': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.RawEvent']"}) + }, + 'sentry.eventtag': { + 'Meta': {'unique_together': "(('event_id', 'key_id', 'value_id'),)", 'object_name': 'EventTag', 'index_together': "(('group_id', 'key_id', 'value_id'),)"}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'event_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}), + 'group_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}), + 'value_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}) + }, + 'sentry.eventuser': { + 'Meta': {'unique_together': "(('project_id', 'ident'), ('project_id', 'hash'))", 'object_name': 'EventUser', 'index_together': "(('project_id', 'email'), ('project_id', 'username'), ('project_id', 'ip_address'))"}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}), + 'hash': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ident': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}) + }, + 'sentry.externalissue': { + 'Meta': {'unique_together': "(('organization_id', 'integration_id', 'key'),)", 'object_name': 'ExternalIssue'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'integration_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'metadata': ('jsonfield.fields.JSONField', [], {'null': 'True'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'title': ('django.db.models.fields.TextField', [], {'null': 'True'}) + }, + 'sentry.featureadoption': { + 'Meta': {'unique_together': "(('organization', 'feature_id'),)", 'object_name': 'FeatureAdoption'}, + 'applicable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'complete': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'data': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'date_completed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'feature_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}) + }, + 'sentry.file': { + 'Meta': {'object_name': 'File'}, + 'blob': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'legacy_blob'", 'null': 'True', 'to': "orm['sentry.FileBlob']"}), + 'blobs': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sentry.FileBlob']", 'through': "orm['sentry.FileBlobIndex']", 'symmetrical': 'False'}), + 'checksum': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'db_index': 'True'}), + 'headers': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.TextField', [], {}), + 'path': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'size': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + }, + 'sentry.fileblob': { + 'Meta': {'object_name': 'FileBlob'}, + 'checksum': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'path': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'size': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}) + }, + 'sentry.fileblobindex': { + 'Meta': {'unique_together': "(('file', 'blob', 'offset'),)", 'object_name': 'FileBlobIndex'}, + 'blob': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.FileBlob']"}), + 'file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.File']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'offset': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}) + }, + 'sentry.fileblobowner': { + 'Meta': {'unique_together': "(('blob', 'organization'),)", 'object_name': 'FileBlobOwner'}, + 'blob': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.FileBlob']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}) + }, + 'sentry.group': { + 'Meta': {'unique_together': "(('project', 'short_id'),)", 'object_name': 'Group', 'db_table': "'sentry_groupedmessage'", 'index_together': "(('project', 'first_release'),)"}, + 'active_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'culprit': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'db_column': "'view'", 'blank': 'True'}), + 'data': ('sentry.db.models.fields.gzippeddict.GzippedDictField', [], {'null': 'True', 'blank': 'True'}), + 'first_release': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Release']", 'null': 'True', 'on_delete': 'models.PROTECT'}), + 'first_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'is_public': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'level': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '40', 'db_index': 'True', 'blank': 'True'}), + 'logger': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'db_index': 'True', 'blank': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {}), + 'num_comments': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'null': 'True'}), + 'platform': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']", 'null': 'True'}), + 'resolved_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'score': ('sentry.db.models.fields.bounded.BoundedIntegerField', [], {'default': '0'}), + 'short_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'db_index': 'True'}), + 'time_spent_count': ('sentry.db.models.fields.bounded.BoundedIntegerField', [], {'default': '0'}), + 'time_spent_total': ('sentry.db.models.fields.bounded.BoundedIntegerField', [], {'default': '0'}), + 'times_seen': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '1', 'db_index': 'True'}) + }, + 'sentry.groupassignee': { + 'Meta': {'object_name': 'GroupAssignee', 'db_table': "'sentry_groupasignee'"}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'assignee_set'", 'unique': 'True', 'to': "orm['sentry.Group']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'assignee_set'", 'to': "orm['sentry.Project']"}), + 'team': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'sentry_assignee_set'", 'null': 'True', 'to': "orm['sentry.Team']"}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'sentry_assignee_set'", 'null': 'True', 'to': "orm['sentry.User']"}) + }, + 'sentry.groupbookmark': { + 'Meta': {'unique_together': "(('project', 'user', 'group'),)", 'object_name': 'GroupBookmark'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}), + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'bookmark_set'", 'to': "orm['sentry.Group']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'bookmark_set'", 'to': "orm['sentry.Project']"}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'sentry_bookmark_set'", 'to': "orm['sentry.User']"}) + }, + 'sentry.groupcommitresolution': { + 'Meta': {'unique_together': "(('group_id', 'commit_id'),)", 'object_name': 'GroupCommitResolution'}, + 'commit_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'group_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}) + }, + 'sentry.groupemailthread': { + 'Meta': {'unique_together': "(('email', 'group'), ('email', 'msgid'))", 'object_name': 'GroupEmailThread'}, + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'groupemail_set'", 'to': "orm['sentry.Group']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'msgid': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'groupemail_set'", 'to': "orm['sentry.Project']"}) + }, + 'sentry.groupenvironment': { + 'Meta': {'unique_together': "[('group_id', 'environment_id')]", 'object_name': 'GroupEnvironment', 'index_together': "[('environment_id', 'first_release_id')]"}, + 'environment_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'first_release_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'group_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}) + }, + 'sentry.grouphash': { + 'Meta': {'unique_together': "(('project', 'hash'),)", 'object_name': 'GroupHash'}, + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Group']", 'null': 'True'}), + 'group_tombstone_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'hash': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']", 'null': 'True'}), + 'state': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}) + }, + 'sentry.grouphashtombstone': { + 'Meta': {'unique_together': "(('project', 'hash'),)", 'object_name': 'GroupHashTombstone'}, + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'hash': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}) + }, + 'sentry.grouplink': { + 'Meta': {'unique_together': "(('group_id', 'linked_type', 'linked_id'),)", 'object_name': 'GroupLink'}, + 'data': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'group_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'linked_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}), + 'linked_type': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '1'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'db_index': 'True'}), + 'relationship': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '2'}) + }, + 'sentry.groupmeta': { + 'Meta': {'unique_together': "(('group', 'key'),)", 'object_name': 'GroupMeta'}, + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Group']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'value': ('django.db.models.fields.TextField', [], {}) + }, + 'sentry.groupredirect': { + 'Meta': {'object_name': 'GroupRedirect'}, + 'group_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'db_index': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'previous_group_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'unique': 'True'}) + }, + 'sentry.grouprelease': { + 'Meta': {'unique_together': "(('group_id', 'release_id', 'environment'),)", 'object_name': 'GroupRelease'}, + 'environment': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'first_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'group_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'release_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}) + }, + 'sentry.groupresolution': { + 'Meta': {'object_name': 'GroupResolution'}, + 'actor_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Group']", 'unique': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'release': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Release']"}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}), + 'type': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}) + }, + 'sentry.grouprulestatus': { + 'Meta': {'unique_together': "(('rule', 'group'),)", 'object_name': 'GroupRuleStatus'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Group']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'last_active': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'rule': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Rule']"}), + 'status': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0'}) + }, + 'sentry.groupseen': { + 'Meta': {'unique_together': "(('user', 'group'),)", 'object_name': 'GroupSeen'}, + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Group']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']", 'db_index': 'False'}) + }, + 'sentry.groupshare': { + 'Meta': {'object_name': 'GroupShare'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Group']", 'unique': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']", 'null': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "'40f794aa43a948a1ad1fd45dc2bddc7f'", 'unique': 'True', 'max_length': '32'}) + }, + 'sentry.groupsnooze': { + 'Meta': {'object_name': 'GroupSnooze'}, + 'actor_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'count': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Group']", 'unique': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'state': ('jsonfield.fields.JSONField', [], {'null': 'True'}), + 'until': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'user_count': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'user_window': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'window': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}) + }, + 'sentry.groupsubscription': { + 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'GroupSubscription'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}), + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'subscription_set'", 'to': "orm['sentry.Group']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'subscription_set'", 'to': "orm['sentry.Project']"}), + 'reason': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.grouptagkey': { + 'Meta': {'unique_together': "(('project_id', 'group_id', 'key'),)", 'object_name': 'GroupTagKey'}, + 'group_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'values_seen': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}) + }, + 'sentry.grouptagvalue': { + 'Meta': {'unique_together': "(('group_id', 'key', 'value'),)", 'object_name': 'GroupTagValue', 'db_table': "'sentry_messagefiltervalue'", 'index_together': "(('project_id', 'key', 'value', 'last_seen'),)"}, + 'first_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'db_index': 'True'}), + 'group_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'db_index': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'times_seen': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'sentry.grouptombstone': { + 'Meta': {'object_name': 'GroupTombstone'}, + 'actor_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'culprit': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'data': ('sentry.db.models.fields.gzippeddict.GzippedDictField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'level': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '40', 'blank': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {}), + 'previous_group_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'unique': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}) + }, + 'sentry.identity': { + 'Meta': {'unique_together': "(('idp', 'external_id'), ('idp', 'user'))", 'object_name': 'Identity'}, + 'data': ('sentry.db.models.fields.encrypted.EncryptedJsonField', [], {'default': '{}'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_verified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'external_id': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'idp': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.IdentityProvider']"}), + 'scopes': ('sentry.db.models.fields.array.ArrayField', [], {'of': (u'django.db.models.fields.TextField', [], {})}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.identityprovider': { + 'Meta': {'unique_together': "(('type', 'external_id'),)", 'object_name': 'IdentityProvider'}, + 'config': ('sentry.db.models.fields.encrypted.EncryptedJsonField', [], {'default': '{}'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}), + 'external_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + }, + 'sentry.integration': { + 'Meta': {'unique_together': "(('provider', 'external_id'),)", 'object_name': 'Integration'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}), + 'external_id': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'metadata': ('sentry.db.models.fields.encrypted.EncryptedJsonField', [], {'default': '{}'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'organizations': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'integrations'", 'symmetrical': 'False', 'through': "orm['sentry.OrganizationIntegration']", 'to': "orm['sentry.Organization']"}), + 'projects': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'integrations'", 'symmetrical': 'False', 'through': "orm['sentry.ProjectIntegration']", 'to': "orm['sentry.Project']"}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'null': 'True'}) + }, + 'sentry.integrationexternalproject': { + 'Meta': {'unique_together': "(('organization_integration_id', 'external_id'),)", 'object_name': 'IntegrationExternalProject'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'external_id': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'organization_integration_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'resolved_status': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'unresolved_status': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + }, + 'sentry.latestrelease': { + 'Meta': {'unique_together': "(('repository_id', 'environment_id'),)", 'object_name': 'LatestRelease'}, + 'commit_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'deploy_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'environment_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'release_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}), + 'repository_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}) + }, + 'sentry.lostpasswordhash': { + 'Meta': {'object_name': 'LostPasswordHash'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'hash': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']", 'unique': 'True'}) + }, + 'sentry.option': { + 'Meta': {'object_name': 'Option'}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'last_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'value': ('sentry.db.models.fields.encrypted.EncryptedPickledObjectField', [], {}) + }, + 'sentry.organization': { + 'Meta': {'object_name': 'Organization'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'default_role': ('django.db.models.fields.CharField', [], {'default': "'member'", 'max_length': '32'}), + 'flags': ('django.db.models.fields.BigIntegerField', [], {'default': '1'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'org_memberships'", 'symmetrical': 'False', 'through': "orm['sentry.OrganizationMember']", 'to': "orm['sentry.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}) + }, + 'sentry.organizationaccessrequest': { + 'Meta': {'unique_together': "(('team', 'member'),)", 'object_name': 'OrganizationAccessRequest'}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'member': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.OrganizationMember']"}), + 'team': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Team']"}) + }, + 'sentry.organizationavatar': { + 'Meta': {'object_name': 'OrganizationAvatar'}, + 'avatar_type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0'}), + 'file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.File']", 'unique': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ident': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'avatar'", 'unique': 'True', 'to': "orm['sentry.Organization']"}) + }, + 'sentry.organizationintegration': { + 'Meta': {'unique_together': "(('organization', 'integration'),)", 'object_name': 'OrganizationIntegration'}, + 'config': ('sentry.db.models.fields.encrypted.EncryptedJsonField', [], {'default': '{}'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}), + 'default_auth_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'integration': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Integration']"}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}) + }, + 'sentry.organizationmember': { + 'Meta': {'unique_together': "(('organization', 'user'), ('organization', 'email'))", 'object_name': 'OrganizationMember'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'flags': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'has_global_access': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'member_set'", 'to': "orm['sentry.Organization']"}), + 'role': ('django.db.models.fields.CharField', [], {'default': "'member'", 'max_length': '32'}), + 'teams': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sentry.Team']", 'symmetrical': 'False', 'through': "orm['sentry.OrganizationMemberTeam']", 'blank': 'True'}), + 'token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'type': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '50', 'blank': 'True'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'blank': 'True', 'related_name': "'sentry_orgmember_set'", 'null': 'True', 'to': "orm['sentry.User']"}) + }, + 'sentry.organizationmemberteam': { + 'Meta': {'unique_together': "(('team', 'organizationmember'),)", 'object_name': 'OrganizationMemberTeam', 'db_table': "'sentry_organizationmember_teams'"}, + 'id': ('sentry.db.models.fields.bounded.BoundedAutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'organizationmember': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.OrganizationMember']"}), + 'team': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Team']"}) + }, + 'sentry.organizationonboardingtask': { + 'Meta': {'unique_together': "(('organization', 'task'),)", 'object_name': 'OrganizationOnboardingTask'}, + 'data': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'date_completed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'task': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']", 'null': 'True'}) + }, + 'sentry.organizationoption': { + 'Meta': {'unique_together': "(('organization', 'key'),)", 'object_name': 'OrganizationOption', 'db_table': "'sentry_organizationoptions'"}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}), + 'value': ('sentry.db.models.fields.encrypted.EncryptedPickledObjectField', [], {}) + }, + 'sentry.processingissue': { + 'Meta': {'unique_together': "(('project', 'checksum', 'type'),)", 'object_name': 'ProcessingIssue'}, + 'checksum': ('django.db.models.fields.CharField', [], {'max_length': '40', 'db_index': 'True'}), + 'data': ('sentry.db.models.fields.gzippeddict.GzippedDictField', [], {}), + 'datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '30'}) + }, + 'sentry.project': { + 'Meta': {'unique_together': "(('organization', 'slug'),)", 'object_name': 'Project'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'first_event': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'flags': ('django.db.models.fields.BigIntegerField', [], {'default': '0', 'null': 'True'}), + 'forced_color': ('django.db.models.fields.CharField', [], {'max_length': '6', 'null': 'True', 'blank': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}), + 'platform': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'null': 'True'}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'db_index': 'True'}), + 'teams': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'teams'", 'symmetrical': 'False', 'through': "orm['sentry.ProjectTeam']", 'to': "orm['sentry.Team']"}) + }, + 'sentry.projectavatar': { + 'Meta': {'object_name': 'ProjectAvatar'}, + 'avatar_type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0'}), + 'file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.File']", 'unique': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ident': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'avatar'", 'unique': 'True', 'to': "orm['sentry.Project']"}) + }, + 'sentry.projectbookmark': { + 'Meta': {'unique_together': "(('project_id', 'user'),)", 'object_name': 'ProjectBookmark'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.projectcficachefile': { + 'Meta': {'unique_together': "(('project', 'debug_file'),)", 'object_name': 'ProjectCfiCacheFile'}, + 'cache_file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.File']"}), + 'checksum': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'debug_file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.ProjectDebugFile']", 'db_column': "'dsym_file_id'"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']", 'null': 'True'}), + 'version': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}) + }, + 'sentry.projectdebugfile': { + 'Meta': {'object_name': 'ProjectDebugFile', 'db_table': "'sentry_projectdsymfile'", 'index_together': "(('project', 'debug_id'),)"}, + 'cpu_name': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'data': ('jsonfield.fields.JSONField', [], {'null': 'True'}), + 'debug_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_column': "'uuid'"}), + 'file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.File']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'object_name': ('django.db.models.fields.TextField', [], {}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']", 'null': 'True'}) + }, + 'sentry.projectintegration': { + 'Meta': {'unique_together': "(('project', 'integration'),)", 'object_name': 'ProjectIntegration'}, + 'config': ('sentry.db.models.fields.encrypted.EncryptedJsonField', [], {'default': '{}'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'integration': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Integration']"}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}) + }, + 'sentry.projectkey': { + 'Meta': {'object_name': 'ProjectKey'}, + 'data': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'key_set'", 'to': "orm['sentry.Project']"}), + 'public_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'unique': 'True', 'null': 'True'}), + 'rate_limit_count': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'rate_limit_window': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'roles': ('django.db.models.fields.BigIntegerField', [], {'default': '1'}), + 'secret_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'unique': 'True', 'null': 'True'}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'sentry.projectoption': { + 'Meta': {'unique_together': "(('project', 'key'),)", 'object_name': 'ProjectOption', 'db_table': "'sentry_projectoptions'"}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'value': ('sentry.db.models.fields.encrypted.EncryptedPickledObjectField', [], {}) + }, + 'sentry.projectownership': { + 'Meta': {'object_name': 'ProjectOwnership'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'fallthrough': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'last_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']", 'unique': 'True'}), + 'raw': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'schema': ('jsonfield.fields.JSONField', [], {'null': 'True'}) + }, + 'sentry.projectplatform': { + 'Meta': {'unique_together': "(('project_id', 'platform'),)", 'object_name': 'ProjectPlatform'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'platform': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}) + }, + 'sentry.projectredirect': { + 'Meta': {'unique_together': "(('organization', 'redirect_slug'),)", 'object_name': 'ProjectRedirect'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'redirect_slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}) + }, + 'sentry.projectsymcachefile': { + 'Meta': {'unique_together': "(('project', 'debug_file'),)", 'object_name': 'ProjectSymCacheFile'}, + 'cache_file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.File']"}), + 'checksum': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'debug_file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.ProjectDebugFile']", 'db_column': "'dsym_file_id'"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']", 'null': 'True'}), + 'version': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}) + }, + 'sentry.projectteam': { + 'Meta': {'unique_together': "(('project', 'team'),)", 'object_name': 'ProjectTeam'}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'team': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Team']"}) + }, + 'sentry.pullrequest': { + 'Meta': {'unique_together': "(('repository_id', 'key'),)", 'object_name': 'PullRequest', 'db_table': "'sentry_pull_request'", 'index_together': "(('repository_id', 'date_added'), ('organization_id', 'merge_commit_sha'))"}, + 'author': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.CommitAuthor']", 'null': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'merge_commit_sha': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'repository_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'title': ('django.db.models.fields.TextField', [], {'null': 'True'}) + }, + 'sentry.pullrequestcommit': { + 'Meta': {'unique_together': "(('pull_request', 'commit'),)", 'object_name': 'PullRequestCommit', 'db_table': "'sentry_pullrequest_commit'"}, + 'commit': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Commit']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'pull_request': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.PullRequest']"}) + }, + 'sentry.rawevent': { + 'Meta': {'unique_together': "(('project', 'event_id'),)", 'object_name': 'RawEvent'}, + 'data': ('sentry.db.models.fields.node.NodeField', [], {'null': 'True', 'blank': 'True'}), + 'datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'event_id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}) + }, + 'sentry.relay': { + 'Meta': {'object_name': 'Relay'}, + 'first_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'is_internal': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'public_key': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'relay_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'sentry.release': { + 'Meta': {'unique_together': "(('organization', 'version'),)", 'object_name': 'Release'}, + 'authors': ('sentry.db.models.fields.array.ArrayField', [], {'of': (u'django.db.models.fields.TextField', [], {})}), + 'commit_count': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'null': 'True'}), + 'data': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_released': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_started': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'last_commit_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'last_deploy_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'new_groups': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}), + 'owner': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']", 'null': 'True', 'blank': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'projects': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'releases'", 'symmetrical': 'False', 'through': "orm['sentry.ReleaseProject']", 'to': "orm['sentry.Project']"}), + 'ref': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}), + 'total_deploys': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'null': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'version': ('django.db.models.fields.CharField', [], {'max_length': '250'}) + }, + 'sentry.releasecommit': { + 'Meta': {'unique_together': "(('release', 'commit'), ('release', 'order'))", 'object_name': 'ReleaseCommit'}, + 'commit': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Commit']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'order': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'release': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Release']"}) + }, + 'sentry.releaseenvironment': { + 'Meta': {'unique_together': "(('organization_id', 'release_id', 'environment_id'),)", 'object_name': 'ReleaseEnvironment', 'db_table': "'sentry_environmentrelease'"}, + 'environment_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'first_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'release_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}) + }, + 'sentry.releasefile': { + 'Meta': {'unique_together': "(('release', 'ident'),)", 'object_name': 'ReleaseFile'}, + 'dist': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Distribution']", 'null': 'True'}), + 'file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.File']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ident': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'name': ('django.db.models.fields.TextField', [], {}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'release': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Release']"}) + }, + 'sentry.releaseheadcommit': { + 'Meta': {'unique_together': "(('repository_id', 'release'),)", 'object_name': 'ReleaseHeadCommit'}, + 'commit': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Commit']"}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'release': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Release']"}), + 'repository_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {}) + }, + 'sentry.releaseproject': { + 'Meta': {'unique_together': "(('project', 'release'),)", 'object_name': 'ReleaseProject', 'db_table': "'sentry_release_project'"}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'new_groups': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'null': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'release': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Release']"}) + }, + 'sentry.releaseprojectenvironment': { + 'Meta': {'unique_together': "(('project', 'release', 'environment'),)", 'object_name': 'ReleaseProjectEnvironment'}, + 'environment': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Environment']"}), + 'first_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'last_deploy_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'new_issues_count': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'release': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Release']"}) + }, + 'sentry.repository': { + 'Meta': {'unique_together': "(('organization_id', 'name'), ('organization_id', 'provider', 'external_id'))", 'object_name': 'Repository'}, + 'config': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'external_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'integration_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'organization_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'db_index': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}) + }, + 'sentry.reprocessingreport': { + 'Meta': {'unique_together': "(('project', 'event_id'),)", 'object_name': 'ReprocessingReport'}, + 'datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'event_id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}) + }, + 'sentry.rule': { + 'Meta': {'object_name': 'Rule'}, + 'data': ('sentry.db.models.fields.gzippeddict.GzippedDictField', [], {}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'environment_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + 'sentry.savedsearch': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'SavedSearch'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'is_default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'owner': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']", 'null': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'query': ('django.db.models.fields.TextField', [], {}) + }, + 'sentry.savedsearchuserdefault': { + 'Meta': {'unique_together': "(('project', 'user'),)", 'object_name': 'SavedSearchUserDefault', 'db_table': "'sentry_savedsearch_userdefault'"}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}), + 'savedsearch': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.SavedSearch']"}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.scheduleddeletion': { + 'Meta': {'unique_together': "(('app_label', 'model_name', 'object_id'),)", 'object_name': 'ScheduledDeletion'}, + 'aborted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'actor_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'data': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_scheduled': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2018, 11, 17, 0, 0)'}), + 'guid': ('django.db.models.fields.CharField', [], {'default': "'33fe0b5189484e7781c8e44dd09f4477'", 'unique': 'True', 'max_length': '32'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'in_progress': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'model_name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'object_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {}) + }, + 'sentry.scheduledjob': { + 'Meta': {'object_name': 'ScheduledJob'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_scheduled': ('django.db.models.fields.DateTimeField', [], {}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'payload': ('jsonfield.fields.JSONField', [], {'default': '{}'}) + }, + 'sentry.sentryapp': { + 'Meta': {'object_name': 'SentryApp'}, + 'application': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'sentry_app'", 'unique': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['sentry.ApiApplication']"}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_deleted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.TextField', [], {}), + 'owner': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'owned_sentry_apps'", 'to': "orm['sentry.Organization']"}), + 'proxy_user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'sentry_app'", 'unique': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['sentry.User']"}), + 'scope_list': ('sentry.db.models.fields.array.ArrayField', [], {'of': (u'django.db.models.fields.TextField', [], {})}), + 'scopes': ('django.db.models.fields.BigIntegerField', [], {'default': 'None'}), + 'slug': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'db_index': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "'5f1628f2-4b12-4ff0-8a7a-47c078cdf7bf'", 'max_length': '64'}), + 'webhook_url': ('django.db.models.fields.TextField', [], {}) + }, + 'sentry.sentryappinstallation': { + 'Meta': {'object_name': 'SentryAppInstallation'}, + 'api_grant': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'sentry_app_installation'", 'unique': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['sentry.ApiGrant']"}), + 'authorization': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'sentry_app_installation'", 'unique': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['sentry.ApiAuthorization']"}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_deleted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'sentry_app_installations'", 'to': "orm['sentry.Organization']"}), + 'sentry_app': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'installations'", 'to': "orm['sentry.SentryApp']"}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "'5f2971e5-e797-412a-b35b-d4ebddc238ab'", 'max_length': '64'}) + }, + 'sentry.servicehook': { + 'Meta': {'object_name': 'ServiceHook'}, + 'actor_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'application': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.ApiApplication']", 'null': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'events': ('sentry.db.models.fields.array.ArrayField', [], {'of': (u'django.db.models.fields.TextField', [], {})}), + 'guid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'unique': 'True', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'secret': ('sentry.db.models.fields.encrypted.EncryptedTextField', [], {'default': "'cf4e9d8c50fa4e3cb85c032012a7a412240a0401c0934648b1b6458e211a9099'"}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0', 'db_index': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '512'}), + 'version': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}) + }, + 'sentry.tagkey': { + 'Meta': {'unique_together': "(('project_id', 'key'),)", 'object_name': 'TagKey', 'db_table': "'sentry_filterkey'"}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'db_index': 'True'}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}), + 'values_seen': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}) + }, + 'sentry.tagvalue': { + 'Meta': {'unique_together': "(('project_id', 'key', 'value'),)", 'object_name': 'TagValue', 'db_table': "'sentry_filtervalue'", 'index_together': "(('project_id', 'key', 'last_seen'),)"}, + 'data': ('sentry.db.models.fields.gzippeddict.GzippedDictField', [], {'null': 'True', 'blank': 'True'}), + 'first_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'db_index': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'db_index': 'True'}), + 'project_id': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'times_seen': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'sentry.team': { + 'Meta': {'unique_together': "(('organization', 'slug'),)", 'object_name': 'Team'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'status': ('sentry.db.models.fields.bounded.BoundedPositiveIntegerField', [], {'default': '0'}) + }, + 'sentry.teamavatar': { + 'Meta': {'object_name': 'TeamAvatar'}, + 'avatar_type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0'}), + 'file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.File']", 'unique': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ident': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}), + 'team': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'avatar'", 'unique': 'True', 'to': "orm['sentry.Team']"}) + }, + 'sentry.user': { + 'Meta': {'object_name': 'User', 'db_table': "'auth_user'"}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'flags': ('django.db.models.fields.BigIntegerField', [], {'default': '0', 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedAutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_managed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_password_expired': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_sentry_app': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_active': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}), + 'last_password_change': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'db_column': "'first_name'", 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'session_nonce': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'sentry.useravatar': { + 'Meta': {'object_name': 'UserAvatar'}, + 'avatar_type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0'}), + 'file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.File']", 'unique': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ident': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'avatar'", 'unique': 'True', 'to': "orm['sentry.User']"}) + }, + 'sentry.useremail': { + 'Meta': {'unique_together': "(('user', 'email'),)", 'object_name': 'UserEmail'}, + 'date_hash_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'related_name': "'emails'", 'to': "orm['sentry.User']"}), + 'validation_hash': ('django.db.models.fields.CharField', [], {'default': "u'XkApeO3npc5vN6XeZ0zUJKCaQXDY9WJi'", 'max_length': '32'}) + }, + 'sentry.userip': { + 'Meta': {'unique_together': "(('user', 'ip_address'),)", 'object_name': 'UserIP'}, + 'country_code': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True'}), + 'first_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'region_code': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.useroption': { + 'Meta': {'unique_together': "(('user', 'project', 'key'), ('user', 'organization', 'key'))", 'object_name': 'UserOption'}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'organization': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Organization']", 'null': 'True'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']", 'null': 'True'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}), + 'value': ('sentry.db.models.fields.encrypted.EncryptedPickledObjectField', [], {}) + }, + 'sentry.userpermission': { + 'Meta': {'unique_together': "(('user', 'permission'),)", 'object_name': 'UserPermission'}, + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'permission': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'user': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.User']"}) + }, + 'sentry.userreport': { + 'Meta': {'unique_together': "(('project', 'event_id'),)", 'object_name': 'UserReport', 'index_together': "(('project', 'event_id'), ('project', 'date_added'))"}, + 'comments': ('django.db.models.fields.TextField', [], {}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'environment': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Environment']", 'null': 'True'}), + 'event_id': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'event_user_id': ('sentry.db.models.fields.bounded.BoundedBigIntegerField', [], {'null': 'True'}), + 'group': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Group']", 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'project': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.Project']"}) + }, + 'sentry.versiondsymfile': { + 'Meta': {'unique_together': "(('dsym_file', 'version', 'build'),)", 'object_name': 'VersionDSymFile'}, + 'build': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'dsym_app': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.DSymApp']"}), + 'dsym_file': ('sentry.db.models.fields.foreignkey.FlexibleForeignKey', [], {'to': "orm['sentry.ProjectDebugFile']", 'null': 'True'}), + 'id': ('sentry.db.models.fields.bounded.BoundedBigAutoField', [], {'primary_key': 'True'}), + 'version': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + } + } + + complete_apps = ['sentry'] From 49a2fbefd3115df11c1cb9fda965644069ce9d51 Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Thu, 18 Oct 2018 15:21:24 +0200 Subject: [PATCH 03/10] fix: Delete CFI caches with debug files --- src/sentry/models/debugfile.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/sentry/models/debugfile.py b/src/sentry/models/debugfile.py index d780d01a22f0d4..aab3f18d2a0107 100644 --- a/src/sentry/models/debugfile.py +++ b/src/sentry/models/debugfile.py @@ -307,6 +307,10 @@ def delete(self, *args, **kwargs): if symcache is not None: symcache.delete() + cficache = self.projectcficachefile.select_related('cache_file').first() + if cficache is not None: + cficache.delete() + super(ProjectDebugFile, self).delete(*args, **kwargs) self.file.delete() @@ -391,6 +395,7 @@ def cache_cls(cls): @classmethod def computes_from(cls, debug_file): if debug_file.data is None: + print('dif_type: %s' % debug_file.dif_type) # Compatibility with legacy DIFs before features were introduced return debug_file.dif_type in ('breakpad', 'macho', 'elf') return super(ProjectSymCacheFile, cls).computes_from(debug_file) @@ -639,8 +644,8 @@ def update_caches(self, project, debug_ids): def get_symcaches(self, project, debug_ids, on_dif_referenced=None, with_conversion_errors=False): - """Given some debug ids returns the symcaches loaded for these debug ids. - """ + """Loads symcaches for the given debug IDs from the file system cache or + blob store.""" cachefiles, conversion_errors = self._get_caches_impl( project, debug_ids, ProjectSymCacheFile, on_dif_referenced) symcaches = self._load_cachefiles_via_fs(project, cachefiles, SymCache) @@ -648,6 +653,17 @@ def get_symcaches(self, project, debug_ids, on_dif_referenced=None, return symcaches, dict((k, v) for k, v in conversion_errors.items()) return symcaches + def get_cficaches(self, project, debug_ids, on_dif_referenced=None, + with_conversion_errors=False): + """Loads cficaches for the given debug IDs from the file system cache or + blob store.""" + cachefiles, conversion_errors = self._get_caches_impl( + project, debug_ids, ProjectCfiCacheFile, on_dif_referenced) + cficaches = self._load_cachefiles_via_fs(project, cachefiles, CfiCache) + if with_conversion_errors: + return cficaches, dict((k, v) for k, v in conversion_errors.items()) + return cficaches + def generate_caches(self, project, dif, fileobj=None): """Generates a SymCache and CfiCache for the given debug information file if it supports these formats. Otherwise, a no - op. The caches are @@ -730,7 +746,7 @@ def _get_caches_impl(self, project, debug_ids, cls, on_dif_referenced=None): # If any cache files need to be updated, do that now if to_update: updated_cachefiles, conversion_errors = self._update_cachefiles( - project, to_update.values()) + project, to_update.values(), cls) caches.extend(updated_cachefiles) else: conversion_errors = {} From 04a9b07db60250ffe1d0688661cc1b69e6b675ed Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Thu, 18 Oct 2018 15:39:02 +0200 Subject: [PATCH 04/10] test: Add tests for CFI cache generation --- src/sentry/testutils/fixtures.py | 3 +- tests/sentry/models/fixtures/crash | Bin 0 -> 144888 bytes tests/sentry/models/fixtures/v1.cficache | 225 ++++++++++++++++++ .../models/fixtures/{v1.symc => v1.symcache} | Bin tests/sentry/models/test_debugfile.py | 190 +++++++++++++-- 5 files changed, 403 insertions(+), 15 deletions(-) create mode 100755 tests/sentry/models/fixtures/crash create mode 100644 tests/sentry/models/fixtures/v1.cficache rename tests/sentry/models/fixtures/{v1.symc => v1.symcache} (100%) diff --git a/src/sentry/testutils/fixtures.py b/src/sentry/testutils/fixtures.py index 9f61fae659dad5..134fe0f3a7c3d4 100644 --- a/src/sentry/testutils/fixtures.py +++ b/src/sentry/testutils/fixtures.py @@ -690,7 +690,8 @@ def create_dif_from_path(self, path, object_name=None, **kwargs): if object_name is None: object_name = os.path.basename(path) - file = self.create_file_from_path(path, name=object_name) + headers = {'Content-Type': 'application/x-mach-binary'} + file = self.create_file_from_path(path, name=object_name, headers=headers) return self.create_dif_file(file=file, object_name=object_name, **kwargs) def add_user_permission(self, user, permission): diff --git a/tests/sentry/models/fixtures/crash b/tests/sentry/models/fixtures/crash new file mode 100755 index 0000000000000000000000000000000000000000..9585caf5922a0c47528ced6f7d17d5738093bd12 GIT binary patch literal 144888 zcmeFa3wTu3_3%AI5(pSLK~bYc9qecmMa?K^A|M$ufpc`Cv7(}4K~Y3O!DJ#>6oZo} zr^m7UTUu?kEw+7ITWz(~KO!n-2#}y67jGykct694ASwjB%=cS+&rBu}ZTr6e@A=;6 zd(6YI?`yBU_S$Q&z4qm-+qeCzJ|2%Z&*N$8i1rX{02a2CX4{4Zo|VOqq1$oGDk%icE@Jc6m>DgFn{r<`nDV z67B{sjaSB*G-*mhr2gvbu1ZJrgtyr1RrI?%jh}>b>DLoB@Md1u(A6P5;Z^n4@V2JA zSHijQGI(X&Ns}6{n|<|lS4_J4x~VgX>8o;c|g{r{v%S5BKWwf?ee zugrwI^Ixf<;jK^8Pr{6aZ~PNJE+NCDNul8QU`{rlhT}=Y@eo#j>9C+HaQo_5%3n3J zi)!BtuP&Wm;N{{a{3iUB`I|K9x*3xiZoKyLnKLHMiqwA%zq&Mj8T2IF6W&c3V7QYe z&A9B_;7v}$TbKqS;aqsq-g({N_2iT8^xOG;uSY2MaJr0yznxE}OwDEFp8c`dui;Hj z!;^3>e(C6}GilPy%deR->FR4QyXs0|y`Jzq$7pyNIV9n4=esK|i(Hlk|F!-oOvB5x zQNrI2@A8H&DfM-JDej3MRp+)h&5c=vWZYhR?ab>k5k1@MOT!zS$$*=|Rib*r>zXIR z*4by(gshB=FV}SQ6sJoNmcPyd?Fygd!aCaHX)2ctlw~OvFRUPkFVL|IY9FAlfM*%kl$nYm(Yk?RP6r$EBU+EvvWU5 z1BmnSqiWAdjSclD&A9sVldiaN#ubF+?|b|Nk0Uk@`~ENbojUd(b6-4lz%}dE_=zj# z=jq>1{f^6oC62#R+AHOX6DpYfPwJV*Ptp`$6#tdh{nB&Nc{eshuDtf7@YR>sUsiwP zNn=H{&7N65z2T(lne|tWo!NLDjCb|S>rPZcPf||0;L7?2Vj4~g_Q(TlsYmFYt*7ro zsO{})Bki1W{P8CSPNcC{U#{|3j;@9i{7!;x-QjJG0_hQpn*2HQ)&}!Q2+`B1j=S4eH#xGjIJ~nDJ zMh!DIe3WwfUxTykYFgnoV4OFOYLW)kMMtL^3+>aOsAqzdsnccp>N2NF8L^YPqg5Ht z`N0c=7X;4_jz7K-@C;EN0&Xt~^ zGty7T8O?tnp*l8rbkM0Ov|kN5GyFwCoSj3xe#qy7|Muhyuv+DvIrNT0>4_EGN z3`%j!{)jl!e$B0bVrJ#0#^WeGD7DsfsKku6<=1$3v}_OAoi?orlU8E1RKpVVa4$$5 zU0HfbaANS%;E#fnE)|;HlT>sbsGR=4$60nB*trL6koZxjx+;0e2jcV}OlVmpt#m(J z@99p@63c$he!;XqgPxm>=GTDHo-Y;e*+h827+$vl?lm;P4B8u-bjDTW}#!g zKkR_K(Gt!jV@Y>1j%Tk2c6~;(-X(-I8CR~8_)$CFSIIM#)BhzLO~(I4GJ{`_&%^<= zCWqfNzbbAmx9sOFdrxf(%yR?uuWi|FG~a=zW#dw?y2Yqj5!f7zj{Kb`(#K4-Rkqdd zGVPoEh0eG__@EfhE(Ny7#N*3&#BArWH>Du_Wk9l9B^Rbn@Qrx#%^1bq!+Lu>&1ATX z=y2=`e`##!_vi$l84LRh!}eG3;h(-pr541S*BH%BWSR;P%Fv0WblN!Jh8+L}ozbel z&L_eFq1eO%YN+mIjTl!v+X`$n?Kj<^kP3!Aoy?%%@4ZZnmu?;XW=1=EH6ASmov^>e zjBoXseLF0>_dmSJ0X2c$Qd_Uafyr|MYns;tjbE%*5s@nC%fbw++zT+qNHNvsnv4>>$9bUBqs}HLU{HRkhI(26TgsaI)2ruDk3s5hg^+E(U{XUz-7or z+Um;!-1!`&agdQ#Y;2a4BB=yV8A&}zd8bg=-r`nKPS6N}hH+Gt=l_`Bn?@e>Kg{oC z@c+Ny_uilX5AplCLXNNVyXwvV7y13QXLYqbIw>KY`gMMv`Zl>^!R|OWH^iwe*e2pepMA4evvNyG>+!?pXy|fE`5V8 z-A78RscDu@zDJk-o-RE*;~VXJ1u)`VRq72vlTK-X8e=4 z0yj4Noh{<@FDJAt1$_4~K{qz)Af3EYCm$@y?9q7KeH}76E2N89xI4U zam~u4X;de}UbLB%X@BJQBK~n^yA=BFKudH1Ro?q-5Cy9+XrEme-7ESdGP|(*?0kW0 z%fMs7t zgU1$n+k*CwR0A`U(rks<@PQh^dK``aI1r?H6Lj*fH&p8-r13B9fq$Am>nwYnWxs6M zU%;A?^U*mXGqalOe0tCRPfUkYC@JBCK;Iu!uCG*E@_q>f7lzY+#Aul zX9jCw!`Br$!zXI2{(`gYG@v zRQL8sW3G_K%nRC|o)ya<>6}w&=ABb)R(2S--zH4y9HmxATH7&4R2R=aR;!MG2B@fx zhpH8TwCxFL?>D#2qp@pxU6Aa*7H8Q6L4L_9i5T^kiXVQNPF^9&Y;AhnyL7Td(6%u; zd8H(?)9G>N=;S}>4mE z9nJm;q$5(?zHzDgNq>FI_DW`)a&q%~`vl=eNU z+P7?dAG+1vMA))A-B!8WEBTwvVgw@MpD;Q=9a-%!4Xi zDsFK1xS3vyF~>S#3E@%CbDYpfxICS=q)h4q7Y(^&g*unj`i<@OEg~nA(UHGl|Hx^}ZZv^)qQ>1Rc>GW}`io_Vz z42mF$xkOAQK8!Z@DGU^uy6x)xuu#0cTCE>(wW88q4aX~~kY9l7jDSnwJ=Tf&%LfwS zhIQMHRy<@Pj7xS`_whFN6ZU_*w^rG$4E3P)nUTv`$L}-8Cq)W`o&d9-L z%f+^&nkA$Y}I4=^{2b!zu-RE{v7VT^)q2&Mq0=}!F2jN1eY@S zy_JVW8b#MC6ybni{>3ZeunbhlySK+_Z(QYM0zS)Bqbb^1)G(1u%{~zSfNFJvQmajy z!6DE}@)b1f76N9F(4eFy&{`8%3=!SvhrTg@CK5*>?fJNp~`Qd`6kd~JnWa|*U?R)CU0@C5Ojs+ZQc1b@%fjj zRE|ADtY*|zr23a7`gjqsVSkbC-x<^uSkuyK8lf-=D0SGH1Y!^UNZH@!f9O&vDu!`k zt5Fv6&obMa{Y^y4pXd+*c-Ar>A@{v~O5?jw$Un=RGA%z{rcP)*nH;)+GYf9Z-!5S@ zHmFJf)VY6AWh17E&Ri#aG|;TP$v+`dAl8uy?jR-?Nt%^Snxm8ExOiiMOd}Na!ysv7W~WI#9nE;(XzoSh=%;-A!phkE4s?3Y=O?HSkH9E)^x`E z$(V*)EO31S*K#f>G+Q=Ct_(WEpzM?Wr9`@;VEWx)>TT>RRvU(Ka-h`VL)(#}YJ3NF ziy^4kT_zK!)9mk{qzogU1O$xf+~IFkkz$!4rTFPt1l?6qfS@UBmU7GKEd+U3Wqbp} zpNz6%_Zl+QI%gLPkj11qUdPIpNyyIkOTU;H?xcw%OHs>q-9PsS>t;nyujy!lM*5r1 zlm4ybaK`{qte&P7y}2BprgY?_FtitRWkxI()wQHyeAq5EqpgJk{ce&>+pEcOVe-o0 z?i$k@>1#PRPjGiUA;oU?2#*+6<;{hWe9OMM(0zsYu)V`whpP)7nH(yJEEHfU8B(9y zhtq#5H9{*93GQODXXNco{_-s6RcOwuaBq?NQteiBb%|BE!?@!mMxiuB;pk`i)JsU95`LV zlR~v7$_pe(4nn9FrXh5d7h+ZJj2x)Sp9>36{9Iq;gCwH3tuS44;BGRrVXzGTL~%!?F>%=x7>QsJJTG%Hsd%@<3)*x88ocviH3Y}rNrL=`E?YFS7h zFa#d0RLWEGS~RE(9|%U7{$ECxJkhR%ecV$B>b@yT`lJNC?~NRyd*Tqu%ly-p9HA95 zitML9c04YG-LL$3F`cv@`SG|U(a^JS)B6=&>t*xblz)SSgWmg0$-fJ}UH)nD_csdU zh`+z8qTeX~4*q8J_rbjXhWN9YxJCS3_rD?j7VcO61(AP)RU?%A`*i>EZ?MQevUbV8 zbjJP4zo2tL{(j^i8i3}Fe@Fg3CQR_3$UiBNm4Cr+kbh*Van4pEXfciccjR9oSbvNB z1LFU*{6jn6ulyrk$v<38$NkE`zUkDg{3H9f%Ri}4$-koiNdEPc9{RsR{>5_S-~4Zo zf6~ZY`FGJb%D>TF@{fV>ujJoJt~CVzx+!l?N9zmlE^>YwEQ!% zxP+Z?#TE;exSLng(mL<^)|Adbt1RzAQpuo?P3OSqkBfRSqoYt)esxnad0?QmEzsJs zdrseA<<5Eerj!41dcR!Yna+j9X4x}ld|RG2lx>_h#GJA#u+caQcq2EMY&_ipynbeC zg;}`^c)jfP>GECjIBmbIb>-x)0jVyh510)}B{rrk*uZ@x}z3U3tc>gQ!ZW<6Zi*zpFB+dnF!6eEC@{=T|~mZVmt@%%WM zM>5FvsXs=yN7CDCz#GilYYIG`)ir%sSgaC7qOTJL+7bEMKm%U-j}UO^(yJ0L8k z-8bF&pipa{fr+!Q7Bi>VP7e_cyPG99apgZjpj%X%*l#}lhp_UGKt|Nk-S2Zqx&lAC*;u2 zbj~C1xu{>~?Zn-Q>*Bh&TXDDIZo=J!yB>EvZU=4$ZY%Y);>V4*T9vOiJ|d*F2S6?2OJ;sMV~G&wO79+sdLP&vb?lm5_7r z5#o^NcuKJl7w;gvLh&0YHdWhuWDz>Yv|rFh#kA>fkWfhS9$AWH zR_vuFV7pf^SXWTg1t)n_m!E|q8tr($4>Lp_yWaw?&@9^|kU{GlbCk!&+FSlJjWO%L zrm#qT<=X5v`a{uFA{AEqSfMp$ud%8@Ry!&ZjS=nro6_zgO}5FgRPDDggT#GDddgm9 zM!($I(8&LXHVm{~Pit^NZwsz$+1~hrMo z*P@BUFs(@T=_O%%iZ2|S0{kigh+C0%%hPVDxJbRQz1mHpF2h>F4^^f;eX@PlG`o6+ zkS>t?nvG1Ne?oyI+ia_YyYv0t$N(h31b3Z83U(~V)Ed#OoXZ-~wC8fhiZmH8HMrgF zqge@+5kMAeBP&+pmEECu0{Ju-NouP7E`;?_mUA7V>^=I;jPJ`cm#1eSgGp`nMXalO znU$;HQ5RM)nq?!!tXz9TwI26pQM(@ZLsyA!i@O$sCFA~wZ{uXg{V)kR2OlF2c}`KK z)|#=A+l6NCCzLreHpn$&MXu$XcBVbdJu?<^>6$mgW$zo!F*2Fa)t&a9_>R0(yBYnU zPh^O>XA6^qwkMj-^t8OZ26qMWvb_<8{i>q=hw1sh0r^;ud~AS!>yeKQE-v!1VJq%d zT;yXt^08q(F7mOV0~h(&(5mHQJ?@Ge`FMzIf-+Tp(}Xfe$k2U432YCvCJu(;(rv%S z;wc>qH4Y=IM9n4Y>+8Abhb5*xh|^hg>zBzx%-GFhKS!bQyDtOFvUe~ZYGboIt=Kr) zhGY!e$)vx#y$ycHq@)VnM%EkMp2TfZJiFe9 z8(GvJVveZy8Mod?I(ywl^XphQu|UxPw}*aWI&(VB5wm^u*NY9os%&rEp7=YtGx$(v zLye`#G{9oOT23vv)%nDT&~S=n*MLZoKZ)tS7N4XKFe|V3H4s5Vz>!6!Cjob|^N`q# zqmkZ#&$R2<@IFH!ID#tKxk&ag?P>7S$)*!9oq0Z?A4U;O7$fHS8a@ffrgeS||1@8Y zxY<{~3+mQC6}I<=%MxPDM1?@tDKKvP2{baJdrvd&@w%? ztvm{W7Q5mX9^;M%2CEv@dq*?0&k&onca2!DtyrYLC+vNTo)6n^*V;S7bea1{grQO| zDlHhubY|0g*U)<(8PUCB3!-fYioRpnms0wLNoc<@I9%LwVTLH<-L2In)vHnwoh&tXZWs`s+NkOiCSMcv^48&Myg9?lYPnfju4!>% zv7pn0bIlQRef3+Cf0N-&i)hW)hWJ!9#F-x{uZDQyXlevIj&0SJSg}UwpES!M19Q$Z zCb&NjR0=7`S3{?IO(!VffZ!QIoO+=`@@7>_hpq)$F|acjVIUk)=QE-ROi&Q+s3}bR zmVlH#S3<-p(ps9J=ffBs$KDgfv?o5^5<>}14+{vArh zf$s<;2SSDB+QJe@GQmDAZ4S>=FYKV?53of3K+0~kXhUv)gfN!hgRtg34CT-GJ;#sT zv9NPw*g4d4F5!G{y)Rt(N!aOUIYD2;fwFq62-`;3K2V9N@*&L?y zAZS+mR6f%NQa>f)lI3ChsIYwq;acRMD&8osHMF{9NR8bNwxN?VTaw!i+$FJ zi|8C`4BLIf&VkgDjU6KJ?0#Vfs75vx><$HPv^6i;C)JwFi?;Tvb&A6dhVL}Ai^BH6 zS{wZefW0i|BI=mnYdB3n*^5H znx8D9jza2?it^AZd+axz4?!(^gJtgu)2A%J(=vDVDUWy==PljG`55#BBh#Tq^kPZ1 z`&jmwor$x>@AL^{9}txF-1iCQ0vO<2IC7z4!q<#wfs8@hNc6`IJAJf4A?UM&O)A%0 zj_4xSQQ%fpV3iTQRuwp{)*jQDaK#_P{17{*#1eHTHk-xkdiOopB=M|*2!3LsI2VW_ zX?7{=2bT8+2IqPsdb(t?`y|E)q)PC)Q|hs?lGt)CWtS0cQW4N^Tw!uH8lDlYm%R3D z*!4`1OQW0_YI3}m_DJ@GrGQe)PGE$I0Wd&!8qp6`L?0>vN=IU=3aUcX+0b7Q00QHN z0cF{1Xje&4PVu}PnaNIk@ChZ;%9%gx!F_@(K4N2$XzPtd94!5+&}eP}UOFyvhVY|vtQzPlKr`?hX)8Doq{)BW|dOm z4F3fxk1V5$WCb0P74%JJtl5u!jXr0;jEF+_HBewlQ^;&_)G|20V<%pCJFrIS*Dd4k zC~7g*0@eDh7wecr|r=ceXR$bR2`%}&(Xoi+By?!5b$y^a6} z3mi<&f5e7gz@9;%)tyC(Y6Y7Ff_(NA2$=_~%$TW0dC9!|2P7+0+;J3oTAJ=2k1uCE zgbksj#(s{KVK~-M!ubOBM60XF_>C(#(tYXllC&#qDf!j1JH4Vb?eiwHl{GDFSI=4< z-6}cSqYK30&umxYOM9#5h`T!aYjLKwM`gXPvMf{{<&}oY9|c|Q^SF=y4hw6ZmmU0A zwKs`*l4A;XwaEb?Sr?)A^ga^3XE!Gu3a19!*>XrC6soIM3lzOL4RBg6 z2}8>MryRynW3-Pd-*p;jw4R>zeRSLeQZhzPP7{yJGo4}V%n;f@f< zSPn$T{K)*kgwQk+vB7xNWqblxhGf_8gNORBM?rg?YB{xWiq;I^Sy zYC&@?NS;rmq>q!MV+gt}?luZ7?*$AAyhDHzW^_lB89Vq_2S_Y@@s_(@)hbyY#fOD^ zJ4aVzLz`N*R~d`AhElM0qiz|S)g}_eMKP(%4m0dnL`(%MA7Plr#? z;TmJfn#ApB&bjN?^Xlxi+9Fd0##yF>Im^@G(zNSKyWcwA+N-CfvrSg6ecmk7ZkUrZ z)VjuBdO(I_PYCz?=R@zqgFK!pV+r@Gwzyl7!+IsJ4#g^KjAnAhjmN|F>61hS?vVs` zSC?m)(NMvX3^RHpm}SOY_el5cahq|4d>zR7%z9Q%N6r6m8a*upMDDQ?O`oTnOyuh5 zuV|JRR-Dpb-zxYo!g8Eq@bW6s-M<49Iqw%%!5tLFb(D01ndJm!@j(ZZqHj@2xngzF zW|`y*tVy3J8VI_qxGJgt(e-MgctrTyEzt2p_Dgv)_QYbz^g}Xr8?||$M1=H*77V+P zhdH!*M+LLAdQpN|TCFAo8V7?$`aq*btDF8M_kyv?waYaNV(+zvPaxc^xP z1h(&|3+@CAT~JNi1K_}1t-Ih>70o}J<|<7l&s9%!Y}_~wwuoM zDzlwW&vvl7GRo&pcLi*VCG*2f7h!v@p6!TQEl~}n*nI1no&Y7N!GV9L|Kha*kkfQ` zD5@DPYWm7Do?LcBRBX=~edVCQd11f%fp|jxp>V=5dBpO6dnn!pAV`Jk)z9rmrzGx2 zSh)L$O*|t$7dyYqnjs}asytc#&GgOY=XBSn`$iA>u!xE%MFrXDT5t4AZ{tAstQd;o)6`;FSbp^3uxhAiPlq$qvoOp63Ray3=34lcN z(Y#RW%r339XV)S6eQKMcO$hvsn&UiLKJ&;URA0|=BrhBJNJ)^o+6O6XAZbH_65s=c3&L&J;3`PE@ck9yBXhjs0yo_bs(4 zLHGGDq}p_mjhxPtrk+9Me)O67A8v(_YeoM@2Y$)^B#1(IqHqfpRs=S3)5l$RAgnHj zCD9CJY7o|!WS?ojEe8>Q$7D$S1X*6-PE1cKlO-R!a#wH)z{e^&;vZrfH4)A^3266M zs5*8VThmEZ%AedaZt}#@+_SiOueX1Q2D%Ur^}X+`{E+xzwCsl zaIw3Rum-#ybWx$)iFB?a%vSKoYA@1BXK;@W0!i`4A;~hl$rB|kcxK>7dceOT3x8G` z{uheOTikblF63@^T{^RWjT*tS|d9! zC*lY2WC6^&ckZmzdI5Bjiq0q|x=6@IuS{3yn$M?FRSQ;CH3MTC$TdBwaP(kzClrDe zTWP7WB=3G178_i(;wThrYhXKd8O`^NBzrIu z6dZRfJwpq0U=85IPW80fG#O0t)nNvhg)m+lOszkxk=^!eob89@4)OieolWQEb}Uo4Ta+ZGFPnKIaFJ|d>M27Hqk^Gtb zGOPm|AD1Y^lq?uaD1p}TO!P_r9a5pu3=$sdoD&>zGq>>WBgFApu^0??KhT*gk*=m(epE$M^#nSxDeo8 zN1vOO{_+@OJEC8CjhYT;k)OdKBB)T}!5@|f=T;G;*C0k!(6`2Knzx{%4yMVRYzl{ zMH>Sfr4p~PXoYHi`hTiQy0>}(?3!Ra7yLYSdjjgG+5flCS^)DtLm-8Xn`VQ+^ z-v$3!ebqhdJ9@wM$@?DT&(pR63vSixnB7=H^!RaGfMYpNrW3dh;0{D!T3kMmtnz2J z7nFBsZ}2g15czwzgd#tdVpQshj`l=CkZ^1pBrFYV?wZf-O-|(Yd}sXaBc1T=k&x4T z`-9}iU~(z7GE|49+A=^!996$T7Fhk5RT>N2JJ>rHBm}0k&cj&z2vCad%F1*ASM=mwXvNETw1s~jk##v)~e zQ)ph=Mze~2vNzc<9xzw~h0K+(mlLLi?R;(LFMjS}l~HIWh{Hz@av_ zKrI6*_e92oV;AnU%65h9jqz>yoE43}-6t~K+{69)cxHT<4re!CmsSSt`*%k%eeLT1k62en$h;A^!b)?vpBChhjVxQehzxPx#j;c~(igqIS2n6Qs&)S}!pfo`pRhvExY`ZYV7v?b|^b|F2A@|p<8 zDs^tTGLy1%l~ zk2u!U?w+tikl|$49`|SeEyo;E?f`hSOTMP>bEC|c=Bsh=z*FKdG@JcCVxpsdg&6X% zlyM3crjwW>CCO;*>8=ezE<6Gtdpro9ji*x9gX3m)`nxU`9dRf{(jBP`>lW%G?IWr>DR_`rRZNGg#k@5d|dQd~K8+$@4ow?^q(@4HPJ zf|UEYc;!KcEeIl7o@y1~oYf;<8RUldhZYi3qhi`cfca!q@ZOJ!7ilIqZIS?)LS*{- zl5$jEU@T)`d)yP~I#r66w#`Md7knu-3LJg0{O>CA!)zq|<8gnbA~zEW@WS*uu*98_ zsm*Bi3HoaBDVQe)s$ju=PXHTENOyN5d)<4w_jclEEx^B$GLTeZ-grdv813(>C@)h{ zW(B&7Qqo@8)l8hl;*uP&u&Vp;cz8?xt? z+tY`!`O^ns&0)nlcer3RWhXgI$$?v?T#Q`U4I!lZ%P}F6LrH1eSt@5wvwM5BlF(uM z4>FJ`xmbZv-u;Vb4i7%`**A*k?KD3lo`1-yP)p}&G_rbv>7A}*^XaB@R$4T#>M5Gp zheI;2rH(|{yGl#uZF1x?E0|dr&S8#j0?!<=JYs)h`9v+2k#8c8XQt)x)tv~r{~yR> z>|S%ic2o!vwjJqw#M!G$VVuf}MkB)`>QZv@sHc6TyB9^t#JawrT(p&%m7*R^H=UY% zySl<%4bx`TN93zy8BBp>fs*QUniA^lH>x^Crk8w8rknQ9n*@_++XU{|WX_jmPU*{0 z7A}@5dA>$Kzb9g8?L%6KTek{!?QU&CMo-T8Nj$-FFej|+iP#d+g=9Qib|1`prpR9Q z^Y^&@8Ajqx0v>nwHP)7MyFzTgSX=g8l8E7Of;vx?i!U}qh3{bsUn|-UFGDAGj}n7G zToAt3RS0M81BpwJuQ~Y2dy4Kj;t&v*Bs%8zv?p*(qlzOg+45O^4>jATUU&GE)&^4U z!4yRj>8WP`0d8Z6(Afg^Y03MM`=dr_{tovUBGb#}9A_pp=yX+*$Arq@WHkOQMIk#RL`uZwEUx~S4TQfJfYdB~s+5Sm3 z=8D5v*_wzqX z5Pe8H$zx^czV)si!mlb93Ulg0jPZ1b5V16UMHpYSV*kkQppU)<+4Up~pU${h`mEiZ zm9B6aZf3pEjeqY4MN3}p{M?Mmbq1ER92LQljo!&5{(B5d=88<_@{Ff6*8CV=y|J?w12^2_OG)R)mv;(YeE9AFXVa(L9<+p1~BP@&LwL zi}|LoZS;98MPh~R{U{tFz+?zKNtR%HZ^;RpmaszbWnX6;n-BMv5?vI0)GMZG40jxw zU=u@Fb{2Qzdg#YSnYMt4+eBXdFa$8C+1s39|23jvSY({(;h9Q~y#`{Yj zFQk9^@#oGTHdj?~Fd=;R+r@)!a6-z3YG z%XWA4+uUl0b=m~`^oZz*z;qB0ChH?WDRTg2a^{n6{57qg;xCy#or0e|OaZ+xnk@0L znQ$DP5oCo3%azl+6F^6?TwTUvW;|6HkK(kTGifLj=Z}NYTYR2K14WDO?hDN5EmfXK zz55q%UNKwgNer$%*{c zoS=Nt-zr&H1kDB>BC#kq{|g0WV)ELsyr=e&+@+MS|ImgCE*(Y&=vw^CS%9I;qy^C; z4T!UFMG$T`z+irs=ATIMkH{%_reQ}85)6%&(_vrdUS$E~7%{#o83STAps{|+$Ryw! zh#PcnQDbYea0vq}?DxxIyjgM>&*whO+KAc;rK%=!i@kI*&21q%_V8Q#u{<^0wBrrH?k$l4g#zDIy$q^iry}D z6!Sz;@(_VP(2CtMhmeeYUKSl9Y%g@PoW~@ib^Po(mi?&}|03ULmN|(wO;95|x>}9w zQg`UXdRmg##M<);C`1FwskkCNrrDs)mo1;FComtTo+2BDmoEJVPM*MHPWKD-;&10$ zc^9J5^E9hKiAC~?)nRz|m;YcZbsJMlYE`)M?fPdp^-v?bU8{f=wjWjfr}(s4KQ@9v zX+25ur^lz}6?Yz5nn-{q5@1HtV>GYDo4%trtNVj8bQ^_>75(U|S?)rHmY%P$M*4)X zvuG&K^z3#X)%B)%(p>_+(u`SRuT{^-WZ#H+iLEA?u^O54XnU{51Cm41UM*2dsT778 z@2}Dl<=0S{;VCz%(yLV&o<_?fAg+&j5X==c;H93t?TOS7kirPlK{@9RMW1XJA%@c( zF`rb`0eKTO7P<#8ZyX37iSeSj#zy^`odzVCY{4MO?!(cNtRIaddAv^kc%e$ZRFcO4 z*G=BQe~0KiDeT*T=%@1pahA!0HEuh308ybq{9PxH&em{0$vnMLnLJJA<_w9sdBGl5 z<{6%P@@U!)PM(|`AdjetB=o+~B|RskrDsoKpPhNg z_(9FhCh;5-MfAzwD}^V^vlHsLe1VXDU24=P*vVzAvz3cfQ2SEudZ^`8SJ?6SYXIP# zzgI-4(E`P>{v$M}F73Ou+s(8O!NJb(w&ukpBuXq}r?F9~Igk zN8d80lH+u{J^F!RuV7rye_I}p>$`6%xkgqsy>C=CePu*?^M7w1zoS(C{P#UO$+1i2 zFdkbI-D@-+8g~a7OIo9?fD8FgPjVy#98#&DFeNMq`OhE}_QJ97(aOJzgS?a17x%a? z{aHv82|$lQ6s>a#9w7t1DyQIY+85#+sP>tU$cEJ{Jrp-9uO5m%hGMCZs^W1)aDxQ| zLvh+)s7nk-bT^yia)hG^YU#5i?x_({VV$472D|B3`@RL_lk}lX*Ap6asfm=zUGH|A zuVs&h*V5(H)R;U8n$L44V`XyIYS!{*uyF4HlI4sw1Q65rXu~_yOtxaIUIZAvB{&|GpNgMYuP;SP?WcYEW^G>M05`CE3mCv`NmD# zIfM{fq&5KPIZmaB$QL1v9^^@H8#64@*?56jWpf9)c{L#|H#c4{5u*m{x4@sGBysT&>J_R6*jA<$xLRwhiUlo$xQ!le~^KfihCnPq{~QLEQu2& z@hdef^^utNJS2jL3F&@u^kZGw35h2Ymr4ndX;R}-T-O9N{#J<-g#|0f2%=>B=Mb1u z@aZ58uMh8Yl1<5{0pcTOF--uq`*8#{s&2(0;Xz{LT-SnL0vUuQ3{RHO2-AK`X^ZKj zv-eUVv;P1JODcs|3UmrnhC-t@+2V*r#pVtLrQDemF_&&0w&ayt-~3*+x#tSf;@b{n z$78)zzsSF-JA}V}4Z;cE2;oJFD&`b=3v-^Jm{Wz=H*qxa(FE>S*ez0od|dU#nERD5 zyXTK!C&COO(}czY6zd8LT+-%VGm}KPWA#h$Ro5$|aMr(5!&Z7*c6x? z+*NJ9Ur&|)!lI(igdmcZ7ts{=(f_3c{74p(Ue8tIddMWMaOXSKLy>^T%IlpGpUi=3 z!Fkp&CZ@rNOkeV7NF|1i#-o#`NxI^R>S3e}Cg`IUA+6--Im{IXy=Wb@ips|^euTNI z-Cg{mv{x2q-%w=TpEN_@!FW0}2P?VpB7kg@Mvb zceMy*Rm37D`&ve!doHB6<6IP2qu-a2B>^{0KQ?Z^i=to}I?`yt9KnIBD=^=&3yf-j zWlR?9g~>yvF4XaPSNKciYaFa5C~tY6lYkJl8%k+Rd@^H_J;Bp^wf0_jEv;q992~Bs zOydAmX=5KOcpUOvtPv{v*vwPMH#^cF9O3lmX*Li@TOM+zO35i+>9>H zLM^@B7Guz`{%>i0ip-wI!LkStEdjzy4fd<<6EG%rGt3vFo%*s1fKsiUmbx!aX93Xs zZsSBuR*~h!fN?+gy<+B*jM9RUw`&_l4p6A+)61YqY1mKiSpK&+livftDcQPtVOpvm zsk87djz~o5t*k0IwlHlVi>|B^^6HnswHCb}Jcoj(jNawf#BbCS}?V+o>T zvrRrzER5(5{n3F8y->{JiN z_pb5{#rL}MmE#lMbE;*oa+PlwKC!WiHm={jbgD<-}_LzqW3`G+tWQ@Y%$O3QnpUit{K5Lm>RJLhWY^>+8gby}7^si>D;E%gMM zf{MZ-_e|4;en+7m@+{qd=#AO_D@$WQD|QmEl?o;S)qQDAb`uHi3c%8RE`HU0$o};b zzw~#y^WGC`NXLul(8?(Hw&^@mzWuDMep;zz^AQ%&nubwaU2F{02Tq9hNqh%@@Ry3e zRr}c$(;Y;7h4>#Pp0ZU^R#*amo%pwE|77v+#83WNl3!3J&m4&t3=njUf1sc z6z50D-|c{$afr^vXn4LG0sl(3k(0qa)~bz_hz?~leCmE6 zU^8@s>|)5%Hq|cOQI#nV~JkW znx2~Y9)XganjSgxHRr|qDmG8=>^lXc?*SbxsU9Xt=1Vby@)$QQI@kR@Bnee^HjZjh!d~6H3upIZM19vU~Y?K zTGk4J9I`ibvsxY?msA&YhH{GRx)Q3595nySJ|L665Q7{slllBV6qb-G=U~ZWaUC(! zyT2pUaf8TV@-6~TKrqylwb6`5n*d&%M5UO^_B8hmknUFO0!+&Jexdu}3e(2pl{PQ@ z_B;n#Pyg-WM%A3A4=!=M9hn6rrM7)6h_GJVuVTVuc!ppz98hx#U9?7!wJ=xlPdTc zNDuy|qWt|ZxU5}7HVKzc0VXx)Cr|Ird;_xW|cOQjN)#{{HK0_jFpjO}o z=cj}pA|--JH~Z`82hjs6lI~_CxUmJ^?Z^5)kFoiAezf2cZ z=!X@S{bEqtYn-#Ojqts*qXbJQ2*ws~P|B-ad7;XLac3c`6Kl%aursnleXvbwJn+dh z+NaK8PR@8HWIS~lkD2jQWjqxbPkF{un(_EDo|25GIO8eQ9({Mzip@5~0QC7}cH23S zZ^d|RG8SUzA4yP*kliUvQy$ppUcD%ldV*#tqs`D>pvxR#<&9&JCkCo<<#N=t5SAS# zoONxU}VJv6cbJU|ZiYRJ^LJXzUs$k_CM)U|861~}T zSR-Rcz5C@JcbSa6NPnk>&qb_id^xd?>_T;U4c>l>)2e)l4i)81xASLO@`xrW`$ax8 zuckzZ?bP@(w9Z$wR!91Hy6=6KDT8HsP)oJJ1?`FZDC+)k5+Xt@b4PUDhk%ZAiSBNn z3sr89l*?J+lBe=_d=+d8`;YX}u~i&%U9vvz7E4{e_*;eZ-|~1;p1#in`;Pb1^B2w# z#t}A=D`6;US#;c0)IZE?EZHPEGB?4N{Rj$djwFXZ3sf{Bu28Vie7?C5Q8d@(NaYT)6rS5 zBniUN)odxg#)Dw)7r)R&eoqmEup~Nn_yxJFMqU%9K0*D5=Y`J0yArSb)ikFf7s``S zL3uJtPPD%7{%tq~dHEoAhrfwz%VY#`*UkN`QdqTlbK~LrOTB80q>8i}bt+!y!GB3EtzzJ~gyoJ*BYDpp7 z+jxqakT>D&J}9Tic3Et=M>+jx;4Gs{`2O3)0_ze<95q$vc|9Vb{?|&L^CZv9bsye1EX#ep8V^mLDzkGYC%75# z+(+-K!JM+js$6%|gJKi%@Bh0r^6sS4brOFAit=^0r*z31)=00UKcUw?RyvIp_Sx8R zm@!{af!D40yZPY}mlYegttP^BxT(j3ao6y$<@U%yrZa{y{4qKRACxnqFG>!35U=iS zHKQGd>D{TWU~s6=;*4)#`_rWF_Yy`QMdLWH8>S*tWjlovwv1+P*uENvXQ{k1K?=?1 zmjJM$D={Imb-NR&t1R!QR@q9~u-mF`dt6vzrPhXxhd#2rYdP-Al4o1ko>|Ow3A$-* zlH00sbM_M^YWMTU5rl~ zk%23)4vtZpj@gc2gZCZ=?t|I+IsN^ja!jwBi{bqbORhtd?c)Vi4mFKQKeam2XcZ_c!BTQH$$b)o4@ z*vWdglWAZl@0@m;_Qix{;TK&&gm`#C^;Ij{$z_v)0&fpbjJI;xq*;go_*t`fuWt@- zL{hdsT{eaXWQs|hOzJdJT~fDd$T_KXq)zz8)GAWVZ%i#Gwc;C7eWaFtV=9=3PxgO4PDE5Z)GoRhl>r>v>A#8W>;63NB zjh2-}iam6x@T^t&tP#COJkCkx{4Zr`+Q1P;*F46&e);i)P(^3gy`AiRK^o56PeXe3Xvj4* zWJNxdwW4vpVGp@$?bnQ3uLAL8Z!qQ>t9pL%@br3uXrBJxB z*owc?uQqn?_dNc^UjQNBs$6447m>NvK1@)haIL+f{`dTsU`o!p-z0>gzFvH&F}J?R zp~hpIsal|)n!Kb7f2;D7h8hZdFBd_{b($65+V7lLbBV^`V!@$dP_6CN^?w4P>VL+6 z$%7ONCX3Y8YO+9ewt5n>9}02T{lso$k}JoM$!H!9y~430eovT%(NEc(3NHULrIhmw z{VtdG92c^ax$S8@3Gr=2!ve>6EZ?8o+%yH>1N#RHH1?P0PYr^fHUG;ds8xSDpV|9@NJ6ORwDiADtLX$@KVBWGo=OKQbCEyP$y; z{}xh??ncT$H&TB8+en$Nzl)x;=j?};zYBXm|39Z?$$n|M0a`-T4P@=GJ9pl@}fd>$_A{ghkAJv^M>BMptvO7 z7TudSyHp;;9KIIXnp2AIG`%0E-eQ?$%ZB5~6-M+gxH_}`zv?ITslb{l-VU)@J$g7K z^|W!jEu~Y>WYcb(EAg`%iTlZ!siwV8XDYN;G_CM8?He@vbYsazs>MD`wS#_XdUvJX zet2us-o*_gbUhDmZ`%928%|6Ib~f!@*f2x~n%5W$E~bU)q)(gn{xk!AE&+i>F9@~0WEdOZrl4)-;rN{agdB*Me1kZ_0JlcEal)-;Ch5LfW zl1oy}n+-d`FlqW6+Hb{UrJl_utj{FKMkNW(QA^Y>DHeb`{gJvIzgy03g zXNfeY4F0w0eF5-IpD%8hkhp`m8mT3!JgK1ZtF~ZMDsJ2*&Zoxh(*;uD=Ef5em*Jz- z?`}9cae?+OY&bM=miGSCxIKt3GyiZpIdAsm)cF9BXHOaWd&~P$O<;3iGq#st>OCv| zes8e-vDZqisO8NdW62hz7wU61meh93yNTH#8%16+?%)z0VzB-Q^$pX&b~Q)I)?~N& z5!1t(t9kSP_@VD)W_AouNfTi%s)?QrBTV6TS@JM>Pee=K`X6vC;0un5Gq-zX(vSC& z`SsQl8Ftx3qxG;|ez>*g#ai#%di3|#GgxmO zCyE|BEO1kLGRp{pOWQ67i6Q(7`RxGE8MxMe_>1J^za_^naDKw?UVb*e8~9ztFVml0 z{NIiBg^Bq(s7$Fl=z?(Um_EcXrH*7>GNv{eM&+@b#F( zXups4Cyngv)lK#4;?}7PSHM3H@yqZO=e|?F;b-$}`D{qPiWnH~=zM63ex$@LH@^?waD@`LgNmWiwzFb`j#IBuUr6p?^h+e|HTcen zMUL~HHD%BPVef0Hw~;Crq?$Jx_BkXq?L*Rz#bc$O&nBFjNjM>!@S~Ixw1bHV+56IS z-4SZ88zr|gnCtE`z57yci=3@Xe9E{GF)Jq{us9mOTG36)zC?843yq}-IU^x5_Q=Ev z2oCPLwL0PSU2Sf`01=EBOeTVKj0mJ&4KNy4{`-&*cO4g1#zD%Sw)7L z-Z#wndp#s88Iwwyo=i0mash-q^Z3Erkd|t#hBfmwlM~d6qxY%h1bZBaNsJdqi}cM| zpQPi}1eYwBy3oojAmE=mctU;cBd}Qn`7aVH^dORlHp8*ut8j9{+#Xd2vu zkBL)tCxm!gb?14iJDW&S-6=OM^~9+YuF53*D4TGJ?oQd5_*!>vA+lR{o|`y^DCthw zKiN-rau`s$vn+v5ue$T7#3#zPu;JiDLiy;+8Z zY}Xz4Wp2mm?oH-)Z+e!bhlRL@vgs*PWW#0M`-krR%jIf*q zte4?v&r{OXmR+WoV)UeFrOBi9{Pn#c9{}PNl(H3iT06i>t*R~CrZ_-O*D;@A_zU|x zdEh_CYRlaFF;}S{;rDBP^2xz<{9gSJe{f;8%mF!mfpak7Bl$^uC*cIYFqjYE_Ze}o z^E(dzu>bHkF(?1j9KXPs#ZTtYDg1=rMR(7ALWd)E7hOj05A0*Mk1K|@JbIive!Avc z?5lF%3?rc2EE*Fl`b=_r<3XOp4?5=zYUrI3{~(TuaGduD?=wOB!a;nZFuHYgK-!|9&B11f0MIP&RG~%F=EL^Vj73h)X9b3Dn2XR3KpYh-)EI=ua(aY4fD%w zSa4?7W>{@bMIqY7@K8E!DPj`gihzee7JaZ* z`toHmj$3{E zIH_}!{aRq#kQIS#HO}=VA@9c_Y@=Iacl0}GZ2A4V))j%*hCCB^E$Cc7n6!tzpM_%0 z$9aD-_RCf${C$U9`Z@Rnb>iCL2A@7P%nkvtOnIEdH^mfLc z?ETQ5kP~k&^1foP4+UC>bn;(~b5Uu?`)UZvb;Rx{{KqA!lwh#e?hLFS(h*o6bS@ei z1mTJPN5{k-^(=p7q8(p-i1&HBBhWcyXP^_hltY)L-fdu5eDuJD)4{M$5VGzHJR=y= z{2Pabys6ler~mx=yT7FRkheW#w+40&=?v@)I_Fk+H`p&x^_|r>{wZCxAlJcvq>iNX z#``zL9xWeI_^73;-WlkiY)6fE>)2Sc=kjBY+)bSY)TzOC1a^kJdty&kR6SDF8gK7Q zeYP}p$WH$AzDiZ+Ui_nD-hYI54zXq%jH`Be z&G6xncTH@O@oKM`SH)KhfCudjp+MYTH>5ojXotQ_V^5y;lkjn|c>5slD}lXgM0m$k z?n}j6^Szz+-oS=H98QDFyz_Toe|voO0p8UdJ>MJnIB&fF(bydWkG||T*T&<;-j87| z_)7Kh=|vUq!BdBN*D)e?1@`elMFw}T(6;4&)L{1oWTZ&5PyWlgH_y^gR|NJ2b_u$7 z^qYLv6T5SPVwCUDkY_#l^zt0YAx2PhuhsMJDa`>KrrmDH`#jM1{ouZBInbpK`n8nr zcrgb!9kA8Ag>iCv!4V7cq2Zx=B<~6diZ0+Q*T48G;6p-Mnn*OO%B3Oi%T(~*uQtD* zu7LdD54cSX-Pn`;I&b>@QStU-?;iW(02I>$!+Yfg53US(J9Isr_Q!iRg!+;iyTcng zy6jVWg^>_jRPpEUFM1#Zb)So` zFuc3$4ieS}b_&B(F8=u!A@7EG>%rdj_MSbj^hK5;LEA36H)IbHV5W6nW9((k( zpFLOf(~x(U0&lkyjZls~dFaib?nuSk4euW5Kl_x27ZsTLM{!VI8Rsq{^&dK+F3^j0O7kV!&%2B4e=3l*^Q>Qm( zo$1B)M|=V{`u=F%IOm4OO{W~TI&?}&Q*`&}=vRGapUugJH%2hW%DKv~-1R8ZDlaWgy(DbhskzDlCXsSm657@d^vV3JY9?Mdr&XYVTE@j?L&?`o0gRll~v} z-UL4C;`|?<-E2aDkX<=6BI1HkK?RaH^< z@-5b;)>`V-)LKRAv50cjW3jEpJ6fw7@rXwisU^Sndyd^tmVosC?f>`xe*dpd@|k&_ znR#ZOdFGy-;|pGVQ#hB2y#xae8?LpS<>fQxnV;Ya(~k2GdM1CW?t?q%vF-K-E=XLC z4Mzx_`-gDP&X;&KFZ=+Qz-w)CyFZBK4!VvckK7E)?XB1=732=7?Rbh4zsg`oDt=hy zNy#0a$uD^J;L8*d(r{3ah*8Sf0_lkd=Kr=_o;ZlsNN5VVcs9)V^#cVxqv^4^QO&$ z_Ij{_Yxn|N`EKBzuktZ%Xv#-}j)$f3)K+i)tVNv#Y+aNgF) zd!OI(WbXDpi3vW{)p%EWKeiI)2S%ql+ky}r-cxn1e>(v)*N(a03SX27Q3js&BKY>v zsl9hf58?dDeV#r0^D}qKcix_Lw$8)+1B9N*+&<*O7!{j-oY(x^&V2kB&vjyUA+6dv zVdc$yy=k2UpO`o%?wcyJBdEF8`)Z#$gVaQ({p$f)Jcx~D#MHUu%b zZV39Y9C>w~kh)7=2*-T`*S?J@UGGHrmp9nqM`-TLp8TR0KjQ8A$9WwyF7dX01-nm} zylZyJWW1mllUDNa0dM9TO~)0U4Pe_)GYCY}D!v0zs986!EjrU{c)U?O3vuB8>e03W3d|{N_=9 z^C&;OhWM10AKn%8UyTBa9X%kt5{nwy5a9DtfGJXdD0GnDSs;P@b{$TB8B!{ZN`8$> zevL|gjY@uvN`8$>evObH_La15h5Rt@t3}1S8pq9os9(;uS5bcP12;=OdGs2d2vM)Vduq2{wO-xRUw0ZVNbqyKQ*@Nzb+~56p)^vK=rX zk?aJRv`>TynPd-x387>s!Gu(@lVR#&wcVAU+1vO#$s*qFNG!m9s$`b4$>{dJ2tPTK zEyo+HUEa)M?2C4!dO93A9_NC+xRJ`$6rUgH+c!~eOX}&vf%eSLoY%41qBi3}JnVz$ z00KW~c2d#Uy)7?cJps4BZidGK*EA9Y-U1!BWl(1N6stC4It6RuJOu28#>*fn0BuDGWpDNPR*~;EY;^O=*xDxk`|ycI2JgM$wyZL_E@;z z+E;$~`h|^etX~%#ef{!gqC5{r=Ynlp-VVq7-s`ctf}Iep2h$oW3Ifl2CvRWaF$3cq ze{%yCC>As?EnI?Vey|{OCvKnvh_#O?qX9n+lIj_P7fJBr@b9r*RM&^-`VjBE#uhwW z#vmWA^>$o_J#RRaBOh!-iEiED@RWa)Z~Hqh`A_80S{Le@+yeccTJGWLqH%wfBb8%h z7;TghWl;F>lfjRUNwU$wG+*ZlGI>o);`?riEcq} zQ#-;~sz3Zw3?`0ZEQS4c*e9rVOEIl(jHV%393#R3D!KoPr-SfDC# z?9d)BIRg#yU!1M&XrNK=rlGn1qqCK}Iyh*383&^{s0_Tc=Ob0;{b&;tc#VKkzs2wWG0+eGO0LSloa`U1*OePD+ zM?6;${~E>Z*^#HG1$lZ}kO#?NFB%FSp?x*ns9pfcv0AqN13xd#XK&T|kK>v~2KI{J z`aJa6wKi;GZJx3(6IETErU0LR!x-)3@Lf~M%rganI#@kt!$wf%P~cLGFEILemv@0( z4t}$@=bp$;;k|Gv2rvldHOT$pB|w;$1FMv$qXA)a4HsimjQ=vw1jXT7O1bAaDq^^IY=&c#M%S5^TZ&jcZ@@*KkyhjiMd?@1LbiU3F{Y_McAz7yu0Bo;6L(}`sL|8BnO%L`LNvdal;rdV z(G*fXsZ${2;S?@%E*=O#is9P;A%^B*II6{`d$d4uq>nelubw$b`-b&zTs4Y~(w9Xy zO1~^;CvP>CVDjdf{26m+&p%P>@f^8x{Z8bt2QAjo&rLjm8L9r$o$I+ZSWZfw1|!Jd zeaB;R0Yb)H)k<2e8CHh*~kxU}iKIV8D@N*id^nKtifDEz`WM*cp%-(b0WU=B*q+`sL z^Ko=jIM_1uCLs;c`mNkAwYYzP0HW?{{Q>74tK5wJ3o!X;3E2WmZK)-&#Ltyqpn<#? zWPxXJB*b6M^B;+D3BUO+i_yDV1Q1B}I(We-_Nr<5+Jh(X*DW@|q`=!7;Sjt_obrAn ze4+Aw^=lxlrF=RblEE_Jf5ThYc#ye0{>&1^46Yj?GtDk}qJYfmEtlahP9HQqJT>7B zJfDqMaZ%&<KZk9AFMo$E~*Ez2Ffb7g`TsVi@MV$G^4VK_g63-iz2~ zps8X-c`uxZpuyTLGCX^-1INIu_2(4eX(H>w%%!&IP-2I~Lj3=a08xl#Z+Tomav z+YlXY%gUa-)mx506L5h`XD7r0~GvMA__f}SXt6$R%+!L%qiDGIuy zU`iBpMZv@W%2J>_muB_xp7j@pC2BVY};qs>wLH-*=Bp)7Ir>- zNwVG8#2Fihv57Y}3C3oKu}L&GLygTaV>8^?BpI6##wHTGlQtOaxz>Fp({ODmhqCLo z6Tw|Q&R_4u64(s3>4Qns_L2b|99wn7~F9rL(J z-HQi33OW|zcjeExUWL&LCZ`V%xAo$e3~ZCM+5TYL3t*Am_Oh~xGd2!m6K`x1jLi^Z zlW1&)8k=FpX1K9QGBzWOO|rJR+-ZBx7RFfyj2^-E^~Fd@!Q_y?Jm2;_pREshJ3LrJ z;6pBPxM>4x2%X-39R3@RA-9yCUO$LyOgHmy=flZN8a6W7@Uun8`S4=9Z4dFfIL3uR z8_rDxzi$3Zpmz(43oek~Jy6auHaoZWxQ2!Uve02ey=UEy(I|d_7kkUz#LlgO=S@@4 zg~=slY*fIFW8MHCx8A)Bma4mV(VYjvZb1Hcab}TiEP8Zj+gh1nn38fwrVQov&C+x( zkeeVeUd8|l$FDvKzdxF1CH)Hxy%$G8**swH(-+bQ<|~% z%86n48bqbf2df_}vI2k9!;Eg(Hp|=b+dJ7ndjs!sM3a*whjf9DFwFcVU_3=RQ8{4< z;tA|V0~!A5@Ti|w>4(FOiwNyS%vl&$T#9#c^pGMO_8d|aOpwg9Awk$bQRK@HY~yp= zd%A6{=bq4SdvOy-IIrCZP|cs2;M*W03}+WOIW+w-mOejRvW@|T|SFU9ETm<{*u#CXMa^&gQTDS@q9dsEgg za?o_GUlhN-JYjtfS3AaOmqhI{bba}-^*O@>y@8!u!zt@?l4!ct=Zsi?dGh*2PGlzY zjuN!CzcKO}VUgE_k%P$VG5j84l^5SMvr*_;aD%GN+4d~FbGJc>d|c=WiQ!=aOgWux-N3x<*G3wlF5qhIB@QbK zs{VP`^|Ku+2pp8{QNEMUwIIC^KnULX+fU%F@CA|!{~{flxBYR|B7JOakq{JHBsyJAz#KmK`2jpYh-jAn)N*xpOkg1M2e@ zpN>mjNY@+A*C9+p2s%Id!>f`1$d5Ia{HTiL2NPN%dxdTUb!dLnKn&6R7_@(g=7;Pf z-U@F|mVHFQ5%a^+LzoN!`C#3|2ka)U^^pkN3kB3M6e<+WyP(HhD zy}W70l%PTDGnXsFNs?1_ia=LkBOg_TDIb)WOJs?8AVxfQ#E2*BDCKh-x;l(0{_m8R zTvc9Je`R@5@{zZBwU4;{JY=xCN{Y>|9Eh-)whlxp#SrLtY@L(VZq@aJK`{85%+y#jQdclyMA9e zjkBA7T>D}3vEjX=At=rXN29}NZ%CAP?r_u9BsN}W!V-;C`IpZ?ezyE}T!`O10uKD) zz8ho7eT(o2AND$Y;Xf5O60bQM7t_1(R=W%Dl&s3kpZw{n$@w(*Hx~D% zV33u##f|Vdy?hh4m>szN8FCoTOA;>Kwy-^S7IHE-)lCRu#Kd7?yJObE_WW58tenze z>cRoSx_=)0@W2br!&Dqs;)p@|E@kKZ!B+la2>-a*6D2{#*fa+}CA+cx3*pOD+{3~0 zVG2y|N@ixqIeq-)sAJ~m(im~#G>pVA0RQ2!kR6AM(e`n`?~B-p{{kO+L??k;2bGvk zl+tNE;AqN}izt6XIOIL4_f|mdXQRuefRcDw5KaY$pJev5?sqt^JqANO%#)J#CA23% zAW7KzM?WAq=bsS4)nm`ueQ?VY{)^9fV#~qZ{w3!?F3us05; z^$WO9f@4h0QmtlboU`>>a>KQquaWO$D@CRearE8_3ccG<6FS?r&^vRx^+2K%hXy4v zCZZs8h2O&b6u;Pz_B_ziumLP9j#c$mERS)k;E zXZgtq-X6e~vMq^E7XTPB&X8f(q^1lkA4cZ0xwmco>+%P@?9sZ$IImw!wsMpCrUR1fK$`QDWr4>8 z9(7>f`1Z56pg>?;@lp5}n9uUEZrm-he>5KKKv?QQBmDmIJ8b#H%SseS3NEY9Po0s8 zYtQkJD`*w>qse{$Gr4Eiq>j!GJS_uqUUMnRyWEIE8Fj&P?+>66`996S-_k$ib_Sdr zQKeZWPmMAxU$r0IxDK+- zZu!EN=f+Pw=xheu5r^{Q_6COFRij=Wu))eZCN_ovQyZWVFhOhSl*X{mJGj@xsg=1W5=mvxcyV`o82|AjI?2BxpW)m8qkIz zUvXvMnW?%31NvV0airSXe&J@M!CNoX3fX#nzyf-h*2}U$5*feg^ky9R3QEV4po${hv8-_+>^6ph<(4TE5?|tHe-z(uyk|Cur?cX9i6w`7~ z=H4vl51zq@{zJLxfGcvH2ze5ETR&>I2R+bY)FcnMMJv~iwaP4{liQV1?-$JR1nk|m zZR$dGOa7Vt#6H|HD7TsaGy5?#n|xHt*|rG90Y8!l-s4hU7i_)lkTiPd(>Q-y&*PCE zTy~3F_t--_ujesIv@_TO<4D~%0pp?U_A7;K4dwd6%i$~}3fY|KymmLdau>ySKVt)! zm%>zxD`?Mi%b};-fPoq1EBzrZ4?Pag$V6PqwGC}d=HH#yG_hs557q7z0rJ<3RzmSKhCHS38o-c0&B{#U4f{@2?_lm5uT0P6S5}Sk*xq27 zJRKq8T}mla+@@g1&ChZz7!Ar;sYK^6SOjX(+6yC+vYR5K#bk zU~xCbhyFEB2%F&6mGE46=PGbV4pue)fpM+gXH>91e6I{?J4?G95uvbfp(-#VHT{c6 zNsmo9_~93ka{KLj!555Wd3IOR|@3f-$8)c>e5YbJA25_q}!X}Yhe!jyS2Ldk-a*p8Y$I=|zR)O640wDgSWGiJ`3&AOh>BLJCmGU0x(IXNTBKSwy$!j?f$u`SUh_t`@zxqki{GXg7pSej3B)&!x|@kEy0D< zHK`4q&WAzh2|VN3^C533&E*@Ia4l&I=3{i}!5wrT;!O0X*)4C|3Y@ur_3pt>7%vt$ z^ZLA#UtcwkqAtnrn47{EZcpRyU04sFEhYYG_*KdQm1Gz$0mQ|2&F}0RDgApV@9<&_ z-#n83@eGC!!|E$!RM>ijt>Z6q9k&Jia23U z6ux61oc-Y|5ekTtkR+rOV&BRQZxlXm2*&t~S-_^+gS$s}<5l3mry#?_VAuw+BsDEJ z@W+x(-xbNiPBWS7UT5yU{6NUFC*1Zbo+A$CXMVM+iK44p*fH*`{Ennyu<_5v zmd6{0^N>d1qe%aKRR!p+mbBDWIgQvCjeBCj3bKrZ}wfdCDw!^coe;n9Z<-lgtvja

y4{qX)Z(3%x=eN6Cx1QGe*4G`5tPaOIy!_a0Q@@Br z`>#XN`&t5u)KRC;Q@%jQb@RZuus9}vZ^-)?qLlVgftS`{<5d6E>vl7H zP|Tg4mja`}rMEp7QLCIl)Qix;_2?R+QRB`)ss31)xxCK__Ctf&;8b zd5HSo9rb@=#Q%r5$84bgY@A#`O)tWdGm5~%9S|dbF2NqQ`AIwG%Q4K3`EpFNW4>&o z@~++WD94|h?HF9*ok;!DeEN%zva(=!j2(GhSh~1q+h!*0BDo+P;V)YMP^##T{wV$= zvO0&w%OQ(b_*}Rs*^flq>`;AT_-jO~3KrkwK=seXxFAxWE#bW|J0`r3z|W!K-8V>h zvGR|_F$&+~4Sbs2Ug*FU;7LkqC$$gB4$Mo`l6V3$Lo9R1&C-7f{}7Qt6qviiz}!UtBM(`t|ZK34Dfe_7B~rgk>LpSylX`{JmrK1$>MNyQ zBlS9|H%Prv>VB!OlKL8{e^=^PNc}3Qw@CdOskce}I;pRh`VCUQQR+8Iy;JJ9O8o~? zzfI~Jr2b>6-!An#q~0ae_4}m$fYiIC{-D$!lKR6^-z@blQh!Y9 zPe?r|^{1r%wA8mteW%oSOZ{1?KQHx=)L)ePpQQeh)b~pL6{)`_^*5y6BlWkX{*Kh& zm3mm}?@RpysedH(KB<2y_0OgLh1B;;{eaZJmij@d3sjW;Q|wZANIgO7iBcaX^(3h$ zOWh^)ky0Nm^<$-;BK5ISA1C$kQg=&zqSRBReyY?bLC5`9w@gPfQ`CRTI+&eRDE*?B z{dv{#gm4a`ziI}d7YX?Y;fav`DnFqo2;D^JAws_)bPpj81$y!5Qh(K73Ef8M?}Tn7 zl#F`QdkrC0&E7SH3JGDn*Iuyy^a!C0LVF2K zB6J3t#NP3Qt|l~^&>lj=2(fAI#WlhGRjUYnfd#*+?SwucbRt@l-nR)=6WUAYQ9{oX z`ZJ-Y35`T&(z}JwbVA*P3JBdzsFD!xEUUVP(5-~-B($E;CZ^~rLfZ)OKE0|B8S+X( zcKYM{b5#!$x`5CNgm~Xj)klQp5gLx+YVRCEX@q#aVAVoGsf3mh8cV2>kc&_gp#(x5 zgucc!p=uMMPYFFtC`@PBe zluhV#LOw!nLM4QbB~(i&iO>~se&~8FCggDdgEhF?k zp=E>)5L!%VC{{3fa|s* zC-f?z_X+h8>LC=530v<=gvJwkme34BPZ3%~XfvT@gdQNYg3w)n`Ztb=8^JLZhgWCf zv!SFz9p1iuort@}b|w5EwW1W2S!~3|0mo7zz4#{JTSTM;p%EznVp|7fF%YR<0CEx_ z8Nf#xk={nY@40Z10bB_L+a<)G7{Ig8vSfCQ?4hX1b-|Y3sWkdY@>i~ zq6_bBy-3+pPLtzKBSOhaMF8_2aSgFG__7F4^>2KTY50|m+)Ly+ zd?UdJ8=#W+j~G5bPHX0GXvRPN#ryj6JahU z@(vIt^=u-`&^3?)8EF|1KHnsjG6(0o20kKuKMUl4Am2J~C`hS{L@%brY-o{^)W2~X zvL>6pIQNl?z@31;q{Us(7AbonJ_dr;Sh>*oRO8aWF(l#^&jrdXd5P8@e1!z4evFTT z`jE&Y_!!JyBF}3?(on;sQ>jQ9YcVzin9C27vzzgqMS!Y*<4Y|_$l|8~ld0`=7y^x1 z#gzdY2OAi&E0;PTWal?91?vjAmNa()SqDTax-#tBMci2KaABkLDy>5JN(fN(Z+uL| z&0wtPA;laJ*`@%9q=OZ-#JFq+@N0ZhnecxN)@5z_p>ZKV)>&I^KLzkl<6>y;WO`*y z%{ER1NJL_r@u$l|uJJ^G6>1$V&c~NWfU1AvUlHWR1X*FEB0!0cU~mkd^-7i$w%C~C zijj2b!N(jufX|i%8zMfKHV-t?NJX!VXB*C5_t=V zBm}JGJ^>ID&095XX8>UDAr(c<7Ef}>vKElm&}<@q66sZ$q3rMcVbh(F!1I|Q-!?X>Bb|Mg|zy?(R#)p|d97T)oDJum~^a~imde|1jvQQ)aM3_XzYLh_%VF~z*F1%ov z1#LHx`+(5fmq2Wv0?7v=z2(4$3F4eblxai;%?HC6s#JXTi{9iDXTQ*h4B&Y9VbEv5 zw4Ds?kaSD=u7ek5H*W$KzsJXVokrw`Na?XKxkX0yAq?$QAT}E`FA%9rvM-^_5@?6S z^NV#Ys9bVbBO3}y#uj55z8Rnp58^#=-6~AX3eT4PBPe#Ru&We%M!i3qDwUdhm&- z@sWfrr0q+M3`L+eGH?jJ?oXQFQ7Qs6@v*fRoA9wim&)3{vJ^^1fTiOKdZ+jqwI8N! zF|p$`6(h;4)%(w^0CEAx9p|T7~Z{J+rFIBzr zK0{7`iRChfn1z6w@kw=NpMNTu6smV+#Om(w>JqL*@Nhqo8>@kanP7T8QMf3l$TVeSzT@hLYkk!wEY*f zL-InGw^k;EiU2c$%NF8RXqK@oLAz+cCdHt9!%yy>YZ6ePA8G}zM z?loip(uJBOe9g-^hl=VYCtCR>sI9CPwP?YEh6*f}x)XatxAeY7aSN4me^6IJ*) z=KBW`+OXS{s(<6J5zc8dkbXAxpHQUhfUqI!1!C&}atl7Glqq>-8^~NFm6g{)?KuXg zcqik#2cP7QL+eA-c@33yV(Qds6_pK5)5;nvOD}IIt(ex-SU#=1rn<7uKdrX9uDYVR zwxM`=bxmdQ>c(n+W#iQHav@RoIp?^iq>G|T|9rTL3+Y-=SzF(@Rv-vb-|TN__KWPA zn)>omf2C-u{%)lvoFEBv>Kot_w#5u7O*GUuxzBLZ!aesKH?V4db$wm2wkuW<6!{yg z>sI8{HzGiPqmW_4muOnsRPL{t+R#{EUR;iN*EUp&+)A*EBvdXbZMr#^>oFtRwch^mJpH_F8yK+r=Wo1Q^yS8*qb!~I4yQ#UMp}x^ySs_y^PCwnf zxZd5+Sh=dYzPSl0Z}d00m)AG)TV2{%fdo&O;2uVQOY1JLt6yCwLu{aU++~$jrJ!p( z%Y914Fj3!7*;wkYZ=7=OVG>$jTU%c@t*IOyD~cQZjZ>=x^kO%%WbxAcd@-$PZIi#U zc3OUQSz~GA+G&(@QDx(*>hj7a?RG(BV-sSY+E7#7DhS#ly$2v=0rsnjty(%h$>>Yixif!uX7&nHY2 zRja4gp#M0R?;d_J3`)_+JfU9C}1$k5O@2IiTX9OyXN~p0&8y ze`2lBy_4etn286pc@0h5$$2XR_dfUW!PmBIShXa>WfO7sc*l^0p^3wXjYx7P$6@_r zNJ6}0_^_dg&g2nEglR{ZcI@G}(!}X9drjgs_}DKBhutn53HG6*u}GOR%r5qih=W-O zUv@`goZX(7U>}}Dw;?bSVTVDy!(J#xCln4#N`yJAaOmiyLXnbMILU1jX~(I_LK}h} z{XQd5cmwS}K|c<22Fw!lvH384m}$NSv+ES_^eJWl=uRDk}0L}S`1EyGlGdILx-Ul;lDJB@i zF97@{7SJxm3JAGK`1g|iC5Zq3fY1QBnX30vV2Wt=RTI|w**-{S} zxfb+e0E!_=+G^Or>{^Q@Y!>}1gm?^Q)>T4$4Ku9;1Eqh0*LC0vX2W#|7iQ0Tzz4z4 zjrd^pbwe(woh@60*Z{NZaZLVUrUkiU%_h2bf;X61e}+5E5}4gELvMgzn0;?T4j9X1 zy~o*&O*Fu~1g5K(vmBHpu$F|`>1*~@K;nmPh%pPZmKGpE&R$ShFq8Uzi%r}bvWqyk zL)?<)5XZQNh)(wqEIB$0Y(?BgOjek;2(9 zN?h+7Eo|MR#dX2a;##bFkHq@VNQ}wbd?$(4&XdG7B@;w)XrfrxmnvGYXnJ+%G!Yk? zBCc{z6Ib@6i!1ul#rIqp;=8WtB0g)TSX(kntZA4fR=3O&t2$?i=HP7M56u=$zD$wi zJ4-Z{oF%@~aF%Fr%@s+Zx!~a(QSUle)HQrd)OKZynwDHKv?Nd1ee*?P*L-og%OjGr z7KxQ8L)D=IQRO;ctZ<(%mIu!ll_8X)zVk%|R*+q3MWVcKsVMU;6Q!M(iW2-4qr6-e zst}jYZi;Unni(ct;lzMS1fAyo>j{yb_Jm0DJt3xbKPje`Y!kzM zPm3uH&xkWa&xkWz+r{Z!JA^B^Q%vspgP4@|tT@g0EXw(_;#Ajj;*_-KL~7P^;^dO& z#6;KgVnW*U!tHxr3~hKpoYeh-7~l5-Ry9LnoGT=DOy z?h#|U_K49vd&H>j7g2y6lM2_kgku@LIrwlZiX-e4j-B|roMQAEd~*P&;Ok3Ud*Ypi_QbNna6ID2w+Y{R zd_zYkquo9hZ9LlXTt|^enS?fdbRqfxz~kVSQdk9l9(;(WaFV1+C`^RuaNSUdz~Kjr zOey2h2TVj9sfBJ=VPUrea>8d%^cCV_3dbC9odR=z9OQ^^=x7)En^gM-z-#agM;h$t zYf|FGXtXh--LBDvo#&urR583!bmk=&%_*cMvZz4xpc=wc^sj`#dO~rjX%=Nqm7%DPApd%FHe+=KU zQ6vj;u`3NaGd{IIETwe%7I_iEXCGfa-=MlX?HhZt0FCxC7))Fh{>lb;J*GW6}}oxOA@J( z{&sw8j^O?n+^e+vP)QZlW&mu%w@kaI8TqV!J4Vi?o~YWzct^vp?`-8QF&dwi*N+Jz->@7C^v$&LOkayc={BA0KU$QsI6d{BK^R_*rc6pPihN zo$PLvZS`E({|Wwl-&6jU0hTHf?vdO1Zz>P#+f=xi#tcy3L?=2G-KYUns?Q|P#(lxR z$gh+m={KnGBF;O^7z6RpI6Nq+@C zLqBs6_uFCDqUk+@xIYHF?jywi8r-|I`}Kp+e+@g=W|h9v2XUvI-P-+>LEPuUJx#ms z9K`(+%3r%*7UTX+a^Li*;%izA-8kTL@oheWJ4r0%hx-yD^@`TAqc2%F;1$O^wQrI@E80Bf8^&b_}i=f4P#gqMfa!SzVis~^lm8^ z+*xx-eb}Js&3G@dJvx3SJ^9VY*A?sUdjtKEA8x}j=`RN^71B?W;rQXcUx%|uIzo%d z2l~4m{@lBi91rJDrXT)1+TURE;m`YXj^NJyd6TsJxrQIjFTjA z?HjcDU2V2$^CoR>(B_@myic1CYcr_L-P-(+|cZ`J1Q+Pqtv-P+uu&8M~byf*i0^KET@pv^C|DLz(m z9;VIF+8nRVN!rZN=3H&g*QQUK-_~Y@HtV#xMw{1Y^G0pnrp+#G-lNTjwE2WKcWU!R zZN8?>ceUB4%>&wW^r`eDYcoZg6SaA!HfL!wOPdR|xkQ_nX|qb3joQ3Io7ZWxQ=325 z=3Ux+K%1Mj`II)F)#gju?9t}?+WcIb2ep}q-^W=0MrzZP$IeunwC}4@?)Z&C;`}X% z{zGlbz{6_-+k-&c>@c&l{ z95b<`#GPBRIQxR^7Dr-(yTR3v(vWh#I3}yOxHxBiPJJCVz1I2lp6;5`rl!Km<(1eb zTwW>8Z|oWxw5N2Agx_y3F7l^ms12{hm5q({jk)Q0t3=C&FVy--zpazLmUv^miHLn> zS!qSF+@7uNE?AtNR$N@ZW=&~X^{Vvr*?`GFF?SIcSJy4C&l4^C6H8nwgbNM{xuxYx z6M2aVvdAABO+~2>d~UXP?bn_)s}(h{kF~^x^F!@cA7WR$V$GUj?DKA_uPd#o_OC5o zl_pv?;lc)~Y@MShT?n@P^5XKU%ZpbuV+**tiIll4fLfA#oTUA8}92Z z_G71faZ_c@@?uz3tgWd4O>7u9dP3+f+qgROFvtT%4YsQC8Z7(-$bBVANZ_xCnOTRi%x^ zD6`f6CU1FO9_%tB$1iA+mr?4^t7Rm814GL|O<#pg^!1I!2)44gT<&J~=9i^|25$bU z#`@L8*7gOh3xe4n6HIzB=_70%bvP~dNGq0JQ;)+3su9b{$nz~KXGH5G9{!&vepach z5+=ybe{YadtQ-D~Vb;oocl}#JY-*}rQD+jq>E96K?8eF_9D1o#rK$Vh5MoYxUg4sm znP^r~JO7R0)z%`|88X=5zad!JuY$#$|Aw$ii=s&mMZ$~rkS4CZ$_<^TI=lAx?&tg z!+{mJbVXgzl|=0~MeT9=CnmV=sN;%Ce>5tdrIL9Yb(gIQ?ShKuXqUXaw7P~4JEeo{ zW*L|~YF4aHDbYDJm@@_mvccRtnB%J2(u#`4;-;GF3hAjQ>PZDIPY*Hduym=R=pGX-_h^-~o;vg0oE!5Q4UtZjdAp{z&fi8`ZE{={#DRk1Q-1L(hh#^xj zut1%X9=f!Z%BNzq>AJ5|_KK@+BFh`l1NfIS!p#!*H#XNH4`up-(ng+3q=%hEA;|_F z+2e6T*oBN;wd`K^YP7Mr37+Vohc-1$m6ewh3loz=q1qw$Qu35|ZK=%Pof2uT(`bk&21>FT?j_PtT~Ul9 zigM6MR(hnRVJxgLlX-m-)rl?jY+ zz?_t&)lWI%SB0a~YF*pxuUun|a>JpnCeE%yop8=n7LQGbIyKiB$8PD^?3W|b%|whW zf<(|smC8XVdEO~)>Y{}^BrW`n=Ha^N4+qVXImJ#-H6(9}>_UR_$F=M89JBd(k^(SKLO z2HmJ0-))N6A^)t+rir9G>Y*7?)XBoVIbx|wfMh2qEmf@{$B}Zd)8Y_~Ib^YIiUz$` zT30L4^blEH-h?3_6A+dt&ocv~vK5PX5nn9wW#?gGUo~92hhr*>md(F($$atT`0A$o z$`z$GK;{&w)1#H*x|3>4tLwx=(}lgZ3IU*oqNmbreZ{v$6@I42X@JGJb0I_QJau}8 zIl)-mv>KDNrpgtyI6W&zoH(G21FOFDIfq(`KTcjEf`}tsykeVUf=Hh|&$Qo#x{=OX z=__9;emFtI#h#cHx7lYEl$LW2u+(3T`DHa`SihP$19obBPur$+5uPFv(!~>T8R zsW*xz9T{TJkQw5g#Ob-}sJB<1^cMSvW*Wg+I;}ik`fSrJ=xi(Y7o``Ar-#oHq2Y%f zGZi~VrHjW-nkimzi^Lr9{rFsQZ~RO-cf{o4iTD{}XZ&n^c0R9KyceI5#$&-s#!Ko8 zautM8t@t25U2I91ldVtEV^E62IQ9$#^RsxagWMe-C%UJKI1!qPvy-%cRN8NYeIM<+ zr$OtR21iam3*iOw#LLT$Ev*fch~3%@eH?gx#7`e@L7W z5p=D11%k%mzBti2A6jSuv@S0-?C`M{qDSXV1zHd**VY!sL|L`CD9svWm5lN?Ls2)Y zd}v6E&}?FS^>hZp#&6E{jLe+Un)2ov)FTxrPLbhIkSa}lI&3xw za4%0W&&0#=Nd!CC52OSfUHMaDa|CD@0qRl`E@#XqWCuidcEnMG&Gp2}K}M z5^}Hxk+<6C7l8>)mVTg?Oo$g}I7E=)C*+7%lV+lJH#g*!iJlR;Vqfx{C6$N~mp6df z{^WGg?#vK>by0rey`=O!@$Lu|fkisU4_*IAA8Y|6-C7#B0+Q#OU*=mS^+n}HdBsK9 z#btTa0E<^fNLMz%d1$)I#GA?B_Xm_p-v)5~|8n#%C&xtpH$>mPkwy?~R9Wrfe>p}qmXY47zA>i?*J|XgT>0OR`jD7k9f|fA z4Mss_6Q@E;>b206Sh%{Dkv70ed(0ENMh=iD*E=J%hTS{5%HraR(nc)t6*rfwUVUX* znRp9n4c;yDa})J0+*t)1-1h{ScZ$Vbh_yXbO%oGj|MO8rYRneRtzV7u4$S3MV(Tb} z2;quKl&42zXq%~b;%0o5o!?UrK^O5k;@wdhqIGnx2#rY>UyPY46Cyjr_flk4{B#tm z;%~(!DdPJ2{KooP0FC}^%(@}QytRY(Twje% z6Q7I|aSO1VTG^)84TJhOfYhFi+>)|r-al9tdbwI}Qwbc6=vXWNU31z%%NdGiY zoG13q;6M`9Q5TFs2aoTZkaIy{WqCdNd|!IHtWgCrwbf{k%fzGOQSzganBY{U8oV6e zJFdQ|Sg}&qTw7Mz2v%;I2`RMjM0lsvyQSX8Ye?e`skE%F`^Kq6y>%i2s}*l>0CD}G zzE$GH3nxl~NPBzOjQ{0$%#JX?;Y5=|HkDdb_1_|?E$bzZQtyH;?4}mTHU~9ViAdRs zpN&`4I2hjNCT^Yp3BAcAfF%>798jVRR%1;0PdB-=bjms6)2R>vE{cQxDfRBpWg`Dhz4IWG zmm_vhz;t{<1}D&5gz#2irJ#Cwb!Focb|U!w3>{pRue@6Pd?Nce{^s=Bgmh`X4VJnP z41#B1_8D^490>q(Yw@FrGs#tbG+6ZUu>Z+KXJ_*!w&@v&Pdt3;92HQ` zJhdb+fW(L00rgl!bSx1lUY|PEB>c}OtAxLGGVr}+hNzO>9F7qNs7;$oi z<(uS{#~-KeIQ3tSwj0rQ-NGbw-%7nt>ODV@g8HGfk$UiFU@dW0WNAe%Yl+V%%^qlj zA496kOg{S3rw_ozj>*xrnZee@eCab|7RAJKlV=Q!LA*RUW^rwKDc1F|lIojdpq1jb z(`ODOt3_GX3qNQbEtt6d^vFt^TCB4;%7wZa11-dzr>74Ly9pyZtY-LTYhb0=bh@xN zH)DNWmuO!`l*DQ;em~h-@MG$M*g6^6awjt(DD{xkTYfHWpbI-wtX3g?>1k0!be^tx zkts-vA}hrMr=ud=CG%J6-M=8*BlXU^CHzYSjluTEr{{`ao|!JTo|%yrEtIonWc$xs zz8veod27V(DVc?s(91myd8mwQ#7k3TT^0X49W_-vdxo$tDrX_a(nn14Sk3c_W)+L= zXJF0fFkUd8<78KCJQG6L#3X-Xa6dfrn}hoqg6sU1Osv#9?_tnvEq-?#A{P#LMf5@?|M|iTc`)23%ltJQY~J>!Y0<({n2&69A1wIqR_PI;_Yc%gj&*ATGzxSunP9}aJnrW znS$^jg&k7g$qb4PTRCD~C0=u2NEZp>IIHe^6De_+V8y)I*wxD~c{ zz_#;Q#@h9qj9cnm&lB#3t_uHy87%yp;WsE<`lR0V0^Ko~$w*JPjBfH!Y>Lz6=)!7? z76wbh;O-y-6Pv0J^WCS-!N8J!E(F#N$6?>$C@LAzO>RJ1%Go#<_yF z9$Sekt7TZrFkee~vD@%jN2}yISH}0Cj<`3x7Ru>4|S> z=(G9y?A-}1@vdNktIOtc!)q|U#}ORX=je0vfV#`x5|`$3xr1?`xGaQZ>x%1hbPe%! z4?*PiK1X+4m%T5($KI0ADTnFn!a1(eD`p$ z{PZzkC>o2QS#gHPcSdNOFk7X^;Iw0Tu=*d2-iNzN>GujvZ*W8Jf*Tca(pz!H&$~|P z#wSx*o(z7*oWax6gDD~vX&9arXLwoTmD^>YXLtsuf2PM8|6u&^&Q<#Tjixuap|{32 zSo~=d6#a-y6~Dm^eF$Mw5huMBC;wui=3mnr+|auSK+%x=TX535PF8Lmn%>~_#`FjA zQ5pK6#1u~Ykfwi1(;M8-Tlr=DR-E)*h=hK>)bs{7^q*PcBfY^-JqxdkI8d+jIo>%M z=Y6=OU8r&1i%a}cjq`q7;<(FHxM3Pay9#Pr-2?XugTRbGdm!!xK3Mz7yLTDPD!8*e z8GI}7HcNOT<_^S315t=yYaVDYy#}8R|4c9M^`#p>E5Ba2Tlw7>gWnIF{N57N-uq5d zVI6=w`8Bu~7xh^AZH>VhKlu&fqasdzgG8W+Gk;x^6#N0`8NR{OadCq+eEOH+5rCp0 z!?WNFFEm-Xtpq*8GdR7G-i42fIO(l8>3JtI-F~9!4Zi(w{4g*m6JAha3TJp-xQK^t zXX4@z@?&t?k)IxXRK&?o4-qKhq;EMx!PT1H;B+T_2|g;~q%R=?MV$0Kn*MH0Z*aOF zjXe4^{R^7j;D$a6;ZPB0{8>bxh%#aX%ypReLKIGso@5~xM#t+=E|y!2aU=rwNWeFUIrFn%je zdfb;S-L`3ZgVR6b55~yP;Asm~f57|Q>35#SdCxoXB^u{_@5GmDocF#HU#oH6|4#fy zjq@IO;y=|m?}I1KuY#IZvHi`1@LypZVeSCV{-!ggzj1k0SjS;LBl`o5GiavQ+Fny$ zR-E#3YyaQY^alS9@HR{QCOo&cBhK)wIK#`*;XRP6e5D(>Na6|7VpxGq9R-E*m1&TgT(;M8-Tjfo9 zD^B_@O}}2#8~kYLyEXk5O>gj{r4MTQdDwo&^c&pJrxCEE--?s}K5R0hm+xtMgByBl zdIw8C-$J0@hcvyx4Sfp%C>o@<;*7t=r`&opy}{}KXv$m5`HFrN$^p}Fa6|u1{C8^l zGc>)ykCwhm(_g6R4Q}YW2td(b`mH$ApH-yXIyJq)>7VHj;-fP3L5V4x^kRvEKi2dH zH}qW=|1y3HPWmoQe;UdWokUuLf`MW^5 zJq~)wpT-TnwLKuc6(@aYnW7(ua!PuG8~UD@_Qi^mzWZWDf1#!~xS?;5+(-Gh;-q(9 zqTFuL^aej#`c6&1Thkld(D%i}Z^apZ%cUy*1fNR3!3{mEr6Rrxy}_@8f6mYNZVKHtXq@k-5Wh#`d`E@&;~M9CD#Sw?Z`1f+HO}`{=>CPq z`OXUQp{R$9kMFGzAE$A?yFz>#a5qe&meJjX_8d>?(w$!gHLYU%^K0P0!Z^a*0DQ3i zoNv4^m|nQEyczshyn54Sp92(*YpOb9plf6;opjrKC4R6Pei*X<)d*!Z;fxT z__1q2#_!Yg1~>Fw;DL%b>AQ$P5oi2;n*K^nZ*aPk|8I)l+=F(HrZ>2ucOwic;*8%- z1d2H0cULR;FPh%qbU&K-`9=`^o`QCp@-w)hxAyOh--M;@{wg-kM(WZ^aqEx!-QJrZ>2ucN2i3L3%4rdiRye?GKvX z;PlV(WA#56J>Q6;-(eRj{ta&ETL?hWAiWi5{K2c0+j32BaQbKbY51rNy%i^YpQit< zrZ>2u|Hk^)qQZ_tzs~X_G=8-7F75vUO>b~R??#YR#HIWsrf~9~)}r8FYI=hk`XB)) zLvO`p`qwG9e`xwXjnhBn7a{;fgYjE&(g$0WTj512{RXFh(r4kLGW1yzQ#k3_3DLMk z(;M8-f0O)On*NWP-rz?|@7DAw-&Xt^+|YMh_+|R7IQb80`emBl;D)}702B?b~RUqS$i2I;Lh>AP-KZre4z!Rep;uZ(HW4SqJpr5x|^Jw5s@&^X`KBVMd=zOP68 zJI24pTQ$!2_UQgYjq}|-;+r(i_xFhZUgLa+kNEQ%=X-p_-_khWaGdTT|-c10C2I;Lh>3e>p+&(E$^aiJY()Y!b zPb*IP(2o^;e5s;0xS=m007ZlGTXE8--LBlmmMVIK(?8=EG4g0sr=js z{}nPOrF|bb%TMfnjh4F=FMo!+Y|k_vUp9F8aqBP{Khtl;nf@+KKMC{<&)@*1{CxzV zXpr8DlOFFQO1H~2y}{{UmLJH7%Fy>&aMJfYpxgqQ-r$Dbyjx@Ft@x4T`53~p%5x9! z!Q|QZYZd>5a+Q9A*8*=-tVj4~dSpVhL3vnlDUWVtb%zO0<0d_!Aq+(MA-xqReQ>jK zdr#9F+|aj3>L|SxCw}s-W{g`Aifxy6(>F4Xr~FOT}m27a~r2gAc>JUH)F)ujt2VobSHU?^KQR z{a51rtmP*Q?uas?ZivC}06tjxaXqQR`Vj7vx4}m(AH4j;>lhe6`M2Un;%5QEv+`F6 zd@z38nx74DCqD-N{bBfV$M9ptnIBm?ymxhY1}Fc_537Hs$BL7_MAHvlq4+U4oFsh+ zCKYkgTXE8-1(jQ-rZ>2u?nY{8REKBqqosFkQ}owq zdV?GKAb6l6&iI2wpomkxAx-~)rZ+g<$$tkWHrRRrhqy7?hsrF0h@gobDlfD0x#|{kpqmYNM)p zQ_)ctzi8C)`x3=O<3vbezT`7wh`1}bqL47MX+Uvg6N2JIT!-e@PK(~e zD*bP1`tu=oqxy?87W!d}-o!*-;`r}ldci`!&!RUm(Qo~>PXCKcFIec!^fuz5iHUv} z(;u~6=U=eUr>yi%O!WJheihRT7WzFrz!RqQO-%GR?a}$ahUo1M>A(yY|~?*a}QpWqK#@p^fVzbE5Mfv+;?ue7k_w}<0Meoai}9cF$v zGJnBQ=~GPqFw+Yb`nZ+8i7EXZOn)3IlG>|ap&z#BO{~5b)30Otn-~jy%Az+h(f8cJ z-<#h>rZf0`kp&= z`ByQ$V4>e*rEg-DKGR<&=`$93Z2Q$8;f?s8iJSG0KL$Uue|!iysDBLKrSty~r!V-G z#m4?2`AJ#%F)@{AC-ZwF=&3ylj!M6m>91#c!9pLm(l;@s-*dMv&rg|Na8&wXrXMWn z{0kQPUMqbQQ~Fz({*Rbma8&voOn(j23l{pV^bh>e548sqQ~G=F(eFOX^nxk==?JDg zk?2iK^gHj>^iMFoV4>d!9`r|;=uJ%YJMPo(&beIIpJ1WyrGMa$eu&=0M8E6%`rQ=M z3#Ry#|E&o0C-hqt(wOLDKhXdFAkzyL`e7qJrEg-QA7=XdnO?BaUq%1GAN>%$;N!}A z{<;=#=-pY2cQdAE&HC%B5Z~;t8-c06+UL*q+^^IA2;Niw5_|{nRY*hrC_SpLUit_A z=!f!aVk+;xAL@69Ry04s6kpMY>~AZGPxJLH;7oC^X54d-#`LUNc|VBwQ-~vf9|I05 zZ_k4|e~;oll~?ew)!_1`m?5Q4I$f5B1dhnaqW=>-dY+)CfXlzxfn-@x>OqtfqU`gbtBV4*Kr z>6=)6|3`KG?P2;J#zLR6=uJ%YTRHyIOfNVp{cff|Yf;y~V4?4^(l;@se-qQg%w3J2 zf}_&!Vfwc)yny%ule;_gnOWuS0w)Z#ATRpThf74%$(( zA3Www8jJm2p0U`od|I%h%XiS5b$qecNH7+AigyAZMVWk+j{ix1FZKEa$hMWGv^g&v>hjFXw|_EqJGu)m_ee?xy)A<+u z%{E}FuYJ2U{SAyo9{&)>m+$lZ9iT`3i+$cp7>j-03dUlew}!FU=S?scd$(!EV(+lX zSnM6%#aQeeKFnC`9sZHA*gJfMvDiD@%~&v`0iInSA3 zEay3sjO9G%C5+`f=XS<&p7X7Yw^^{}f}<|9?~BKc&CdzcCj5|Kp5B z|NmRYqW?eU9lCs?|396v=>LZpi~j$0j79%{8Dr7^ml%uw|4PQ9|Nj7E(f|J)W6}SA zfwAcSZ)Ys}{|6Z7p3&v~1!K|o9D1cLkLY_&U@Y?da~O;K?A45AKeCCj>_^g!W&K`Z zEbI5H8OwNkEn^vP|DLgoPhTW_DE4oxFTRbj=#L)|%=+Ue3HM+<#OHYrj>Td}AB6ek z1G@ajGZy{*xs2s}!7$_aM|J!O!5`Lmma*u+F&ruWBA@vy#v*UJMc&_`>7Qi0_fCyZ zMcycXB41k1Smb?gW-Ri?8yJf`?;*w_UpgKICVnEXThCbJZ*(Iay_fyn7Z}U_?Cj_0 z_p*LoVl4XsTs))bWxf9+dH=934=$fn?`8cy%~;mQe<}Du9sj$GWxsRiA(~#+KNE~) zJ$nUXS>IjHSk{}jGM4r8FBtdo`s!q~B`UwHZ`U%G^;(wkFvs7?SoE{^F&6#znTKh9 zVsCpXW3lJ`C}Ytt|Ag_b({=ezK3vm_efsMd#|QQMs~L;^>{l3zJ?t}rIsOID)BMC< z^-9KK|N0Mt&({3!W-RNa-sfw2S^uqPEbFm(#yyW}`j0W*&GG&5i zmi_kSj75L{QHhU^htCxIE@QEm=s8036Z?gW8H>Hjj|9J7)1P#tjxY8K6O6@v-b`S_D;q-Qu_Vp7>j+?lZ?gQ>&+Ou zsC|n4);}{Iex0U2{}}yV?2~30i@nvKG8TKiuQHbN2|pu@G!N(T@q`z0dRV7WeS3_1 zc4&MfV>w@V@Uc3+oY&n<7(9EK-@EyJ?^T-qR>4_~PkfQ4k2C)~;~3+c2!r1$=Jzze zm-DTE@M29b=PlpKSk8amNf`8TrtdjU;{nDS2&15KUiwcM%lX>}8OwR@eoPn?pBCx# zGsjrYZ{EmQ&W|2;f{rie@!!i>&bRNC_Y0a|;zS)^&aYp^SkBYm!&uJOo_vyyFZLcT zV>v&517kVQee}sXzMSu0%UJ9$N{q$+<0Fj4p5s=D@9O+MCGYurp79dRU+gC~GfvIw z_-|t@_jl}JEcc0=_EJqR_npiW#uz)y^ZjmqFZZb&bBc~H_qCK6%l#UE%UJH0xmV)j zJJI($;#5s9_r(k|mis;C7^gV?b&TVTzr$GWyLpCjiQk{stNDvPZ;7$ocky|~a=znn z#_^kU{!V$Brr*o>GRAVAWC!EkFYEZ%F_!agcMAO{^!r0!uKCG%weuN^{Qh#rq7S&9 zvB$l1KyL9{)ozC@hH`6l~{m@@C7X8cusj9b-A~u!pgnKlvqNIS=yEvzXt_I=^cf%XyP+jCXxazkd&7IS;a1;&c1B zhcM>Eoe(-46vG8&nD?oFmAHScAdL6>PSfu%C43Mj9sE)Jg2J(yefial<^1ZOGZy*A zE@03RsrtLY!nauXehWWs;iJ!K~qv3;%_MAF*)ayhi#fEc^`%AGe}0{u?ZOy@fw( z;cqLvqgMXC*887Z_{6@(^!hBkM&UZYF$=%m!k1XsRk+?h->z}(`@PS?*J@nL|36vy z>lVf}mc+ak|0fncB;NS`g%&M3twa5>n;3g3xCPNU$gM77QWxY zPgocieNuhY%JV!6!*@f&`&U>PSI#!PUuofC3%}OFTP%E;g=Z~XwD6LJ-(%tHEc|&3 z-)7 zQ49ad!iNtu(jRZ(GcA0cg;!emd<)2%katEW+myK9BGPgfAj|3E|5KHzC}N z@D+rwB76j>XK_$I^Z!b1oTBRqoeV}wT${vF|`2u~pV3}GL_e<1uE;YoyFAp8p9*9cD` z{3pV15dI6{X@vhq_$|US2(d#@w+II#JO|;q2!|jXhHyB-^AMho@B)M*5RODR3gKvk z9)x2MUWjlk!ix}IjBp&n@dzg%oQQA|!pR6PL3k;`DF~+`^dh_r;pGUYA)JnI2Er>4 z&O|s1;cSF+5Y9z-CBk_KD-ik+;s_2x0-+z_R+NWiao`m9&pIvf4Itg#!f)szQc&f4;vzC&mfKQ^fOAtz0|l1 zpPc~M*XL8D2$%d*G|M|c{R;fX`|tyz*6+ke)H>LvvKZNW05P__A~d_vKOnm44LP^ZFPfvAkqH1)Ltd#yG~qR98oKJf~(vQF}@P7XYbiqCYS`#)n9|(zVxE5;XM*h~^ zMzrUeNDXvE~1CcxEfB;qFSZ6ndQ@Shun2Re9Ihit6j5%S?1%#o&7!?$oW{~eK7 zH66~ZWSf-4j@fLq^2ZG=Hhuc#IAabjy6xix<;jLrxGyT)pM^y=MkUjYTyP!+Yd!OANoZ__KehR~tMKTAU2u*|Nr(6_kD3om1IsHwq6-6xO1iTGG+F-o2J$9r;K49Q z_aXIYt`Tdl?nB~MTyHVsmB@3Un}@6AWXR|@Bt!K@UcoD;;ltXA;wL&d)c|Hu-AG4f z*sr>8Q-8s2@)Cc-p-!=*^Rt{EMp?{b^Gn!0$7&I?tNw;a~BM{GH_i%h(#F4-b)-|SgD zI=SxEMYykAtkxSyh#UFHY*+JKU&)LWrpe#3m(Ih*_<(XTUUL~Ad1y?;ea(hFe@D$x zA|B%SJ30eZq}q0&^D;SG3far&yp8F+H&&?CNjE0i@gX;Q)$&Tz%NNT_O`Y6Ft|{iI zI%zjKjxSZJUcNgGO6O5`{_f;a{MDvLu)2_`4Ss^jp?&14>%S;no%PD~l9uynT1td& zC;c;jLQm9d(dD+6AgSp}|1)Nh3(LhkidU{eL|=xuKDm_YXgbl8p7OgMwVFuq5xr6o zJ8VzquRRj_w81)D&gy498gC;XO;)-AoWrxRN|u@A!d{JFWykFCsCT$^ZsP-nGt|zI z=|gTywKac4V7br*mi^nu3iIh)b_$cw#cRUW{Bc)>EcZkH;kGEC11VqiKUXK zxsI)>z3xT=D0Wv8sLP^rA!AWMu?IGlyh4+H89=gfBv-7EpXlJ896&+3BWjwV;=BNw z)}rzNq9&t2Q0%sPRFlpcz`M!Dw5}({Cep2VWw<}avS~I|sgDR9Y7XDL6k z!AVPeC95rt)d{lM79Oo*(PQzW?zJYNUaheT;203X^gx^k?LttNF} z1Hv&5S`LTwV07`7SWKpKsELRqt3k4*GzE9-{zOviX7f7lgU~8aU2myobJ=Rvt2jz0 zULfE-6MI$QRzyTx^L!sE9a+dQ6;|!4`OQN1@0GEjNi9ZV1HbE%@<_4lB^S4` zAh<=2SJmm(R5wjeSK$6VQi_RqopNxXJ=)cz*;u<&^(x8Kd_Gcgy48_TYdr3zoP>Ek zWsyX6U9_ z9~-5w>y<~=>Mmq<4GfXK>JMq7b%yoEf-#V4jb_?bT$AG~!#-+_v$JK(*yuVckRQiZmVur z>jI8!9>HMUsz@A}>aKNUbJ7VdI*$?aty0FWqc($uGt>RMC9@{oPCj0($JNfVIYVoU z#cHKmPM0=&Gg)l3%gJr2j9MB@j0GHq@$W*^bU88JM%;@d$IZ_|tnOvzsva+aVs0i~ z&CpX3wwKe{iWhUUmG$0CI!8+2aa!^eGI*FzXA3xAc_AI=T2sQZR*&`aXasxZs<&aT znx4!dHsWQ~*~xTy3G(5K)44hIG*vE^ymD1ZNi+#5v4|(p&a+UYZIua-rUHAUA4~YIw5VE6h|e$QY3}q!+XKxjYF2X|Y$x6sNL< z8H%!H^ZI($DKA>okb>Ajy{4f{<+Ty%2#Y{T4rkt52*#gGdEro?_VanC| zZ#C|&8?W}e7)=wC=}Hz0*=iZdk7d*do0(0QU9_`owKA4TCh=@_I+p`Ws6gmJGLfz( zb%$X#iMZ>|dl|fU!OwFu7+Z^lvGtPtu582BUl){08)5p%Z>xX;|s;hSIH`Oi#J0(+T(S>S~PmdF*Pc zp6WXjV|ELtFHcYF#OevgaCE`U;$nI-JMTC{_;)T{skl|hMBFR}h@_4d$nIjB-BdcI zlAp@y-$Gh7-ogU?VEDq$tWr%Es(L&cp_Ddz(_UE*^R5ew5!x+RF`}Z=zsW1p0EO(O zb$L;|`jjDWCNbd^^n4A+z1h%x-t5(^(S97*A&A(q)YP^a%T{atZ&OTFOnilvXyMo~h4(_H$Gg<_i1i zd_E09ty_WMtM*p&$rR@bRSC|9+QY(AUvczyw?qERuk1>I7zGqWg+dR~RP9{J8q zqu1&OvzQ8})FXZ}pLc!i@Qk9>V^DoS`(MU-rA*b4_wt!pG!6Qt%EfQKI8QI~X;f@} zuAqLgxIn8dWvAR}%yg6)AkA4StAut%IZ9WnWn={NY7zVzo@OD*upVItJ>{Xr&7#>+ z{Yv}{c$qkl<<+8}t$y6dS3kq7O?l)@>1T5VemYmJ#JFNG;HTAsD&|%SQWj)BlSlFK zn^V<7|DYPcFV+^QugX#-Q$_F4zmrS4eQQMjt`_rI$Q0CPvw9-h0#dKNsg%4-Hl1^I z%TPv(gvf-9M}}_ofW+>!g63K2+m@d5%6%hh`PMgy??oz|^Qy&SuF{7lTCDV~SkX7- zl`6HDeU);iFQbHheK8E^*(pf;+-a=zT$NOLMJ5xSyE<3Vm&;CSjw{k<1+pxjjbS9{ z$b5kK6N{+%ShHLQrhrUGPa$046Ofa>!w2U1)Wk?G$E|~8dp0KEVR3!Skf9y&5}f&V++aT zWNO`n8>+Nww;J22Lc4_B(8l>nh<#e~5=QQFpogoknlZO>3iZh{zv`M$3w@~e0F7W!SvIYAzc%eX6ghZ);xVN`w5nWq<`vg z(h1`lHt#G?3)Zw(29-7}TbQhZc`3mKP27DOupAppji6)O4I&5bou&*iE1aQ_7&z_kYgh3%XamUKy^6b&5W7I6@#;~qYa`{a-E0#>o zqmQ^&GRdU_(6X`ZA{p`i|NbP|I6z6Ib2CNA6rlOp|HXpnLLK7|Yuh0o9U8RIj~={; zYHWg?*nzgN#?)dQxZ9X^HiLs9>}k4DMBTa&8(DzXR|^A*aEa*aMJ|l9g(e zh-y8J6(6v1Tsf*IbTx=T0BUJb#uF|Yl_4G9|E-^dl}Z&C-2B(qR|CBD+6KW@pva(M z8Y7L%YwpFdL*z^?lIsetg1|`v3ey+{KCR2DT8h6Wf)Y@ZF~Qg0=py zA%O%^&>~V2Wl~yzP`s6LUe)U))yZ0E?1gW?Zz;UW_hhy2i8J7T6tIID^v4>C)s@G* zQtohN?B^G{IBxHxU$Cw_>iW^2tx#!NyOwcDQNOeGH2UlQwR8+Fv))%#pu6y$tJQ!lf~+qK)xA;FOy0~ z8afn5G%Kf*R?nIl>v9cvo5+ZLwbW=>Oxn=G40MUPoLkedhRl3ER!u&qFRr7_K18IN zjBItE*uQ4q%fC-SNrY?V>3mpO_Jsxq)L1`w+^P$XcqC9dDAq`^kUf~vKjP&@Da3NS zA`O?pYcXFdOK3wN`6}Hu;`d(-6=q7{%bBzMD7qXR5U}hID75d!jODDVoVb5=6tJucTtnIEIsQeP<6pHUR))2>i1zc?P|V;w zUKPi}yeT?@ms}CAt2^s0n}mWa``@SZVvp*wErqqDQ?O5z#0P5mh0EyoX>)>S=XYy=0 zp-w$?$F$ZoSnMW8efE`3u|g=(p@n+ajh>xu+tBX^yI`lY+Xr|xF;1u9x@}h6-P=@j z-KmV~iwD{ieFsp7yJ4*9zk23 zfYUYpQ#p=5z2%FZ{d=T@pMAo=1?%@$Ous+({pxJcezL@Z4M4!h_m3dS$YYJuzueoq zhKkSDtxJPQS?9Vim!E`LbTaxjGgwZeB|NcAu>hO@stfzI6&-C|O-8h5#hgow)m3)Y zB7WdWpjl2#dYrwjleh3}5NLl_rw;OB5bo`OJR3}B%N1qywjo`{X>(GO<}hw5J2)7( zk)dDAEh;zzRmzzP6tzXWA;3PNyxvK`T6LnFE>vJM zfE%{9rPhU3JeeAd`w>F+`IvOCSKF(;U%2Xpe)nNw|E@9oS@m`cx`4n#4k9^HvxV65B z<#*#Y8M}e@$IxzOi*Co&USlKm-ol!{={QDhxyDecKAfRZ`Qecw-XQ?tE)B)4?;5Dx zlN6d{UE&%VW&hpM(7^>4n3&qU$HTaPrps4~bpM8s_O8+B`t>W%*6ktni&5+$02@Qy z*XHE*r4C16o0}dHY_C?4+*CCjM29gRuE8nvW&2kR_PLwJX(7C+44c8wLoapZv7IM9 zsMvWjZ!)rO)}i%G`yR9Ehn?6n*-Rd%@zpg9uuw=jD=S{Z6H286_gC$BFN^38tu4o8 zwRM53ic6I)7q{W|Ec-ewRi%#Dc4`v#o0b~lf2cLRU97ruHOteQ{>lsLx}>f&VrCUO zr!`!g1W6K29U=Ew@ikPf794gF)H2#Pe>A46r>y4bM&X0}cjfvknnSz7s3!Y!n%6*+ znYN*(pc<)N%@%%rlSQ(O%jY_|kj+{4HE_$g%FPk+{eHOe&YoxdLcPHlZ&_VWkc)&`E>3+3$s*NGXn%PUv@6#y9(7_Uv$C|y!$x8G zCqaaZRHZG6aRNOQwHoGxEApmBCLdRIC));gint&B^Z9Zg?uw})Oa1lR=W$y{=z)+% znv0gk*9UjD zPy;}}w=}GFLOzBax7qiw~tD+C37y|R^q7rPcvJfkm zO3`WcfjmvFsBvS9y2`J{w8m2np9%M(^_$kNS?{_V@lol_HCgD;aulk@)cmBcAn}k7 z7jQF$R|EDt`fuAE>!Pz)& zKuKs0G*H$hqw4ZnGO|Z3r|oIRf11Rv{|dh2#h!Wn8yx?+5&z+mfMYQJZKxBFr+}${ z$Tx{b$wAPun}94tN-wg7LF>x%D<=Jg@mEVgb^5 Date: Thu, 18 Oct 2018 15:41:21 +0200 Subject: [PATCH 05/10] test: Verify that assemble creates caches --- tests/sentry/tasks/test_assemble.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/sentry/tasks/test_assemble.py b/tests/sentry/tasks/test_assemble.py index 5298a46aee7898..753ea6d77f9c60 100644 --- a/tests/sentry/tasks/test_assemble.py +++ b/tests/sentry/tasks/test_assemble.py @@ -49,7 +49,7 @@ def test_wrong_dif(self): assert get_assemble_status(self.project, total_checksum)[0] == ChunkFileState.ERROR - def test_dif(self): + def test_dif_and_caches(self): sym_file = self.load_fixture('crash.sym') blob1 = FileBlob.from_file(ContentFile(sym_file)) total_checksum = sha1(sym_file).hexdigest() @@ -67,3 +67,5 @@ def test_dif(self): ).get() assert dif.file.headers == {'Content-Type': 'text/x-breakpad'} + assert dif.projectsymcachefile.exists() + assert dif.projectcficachefile.exists() From 4a1e6e8dd9808c56659c41e76b46e033f95469d6 Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Thu, 18 Oct 2018 17:02:58 +0200 Subject: [PATCH 06/10] build: Bump symbolic to 5.5.0 --- requirements-base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-base.txt b/requirements-base.txt index e861b247e7910a..f6f1207e36d7ec 100644 --- a/requirements-base.txt +++ b/requirements-base.txt @@ -62,7 +62,7 @@ sqlparse>=0.1.16,<0.2.0 statsd>=3.1.0,<3.2.0 strict-rfc3339>=0.7 structlog==16.1.0 -symbolic>=5.4.0,<6.0.0 +symbolic>=5.5.0,<6.0.0 toronado>=0.0.11,<0.1.0 ua-parser>=0.6.1,<0.8.0 # for bitbucket client From f42f00a3997db9cf6dcd3b386ccb5ed34fd3a900 Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Thu, 18 Oct 2018 17:43:49 +0200 Subject: [PATCH 07/10] ref: Use paths rather than fileobj to generate caches --- src/sentry/models/debugfile.py | 43 +++++++++++++++------------------- src/sentry/tasks/assemble.py | 2 +- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/sentry/models/debugfile.py b/src/sentry/models/debugfile.py index aab3f18d2a0107..49d88778e7c9d0 100644 --- a/src/sentry/models/debugfile.py +++ b/src/sentry/models/debugfile.py @@ -395,7 +395,6 @@ def cache_cls(cls): @classmethod def computes_from(cls, debug_file): if debug_file.data is None: - print('dif_type: %s' % debug_file.dif_type) # Compatibility with legacy DIFs before features were introduced return debug_file.dif_type in ('breakpad', 'macho', 'elf') return super(ProjectSymCacheFile, cls).computes_from(debug_file) @@ -664,7 +663,7 @@ def get_cficaches(self, project, debug_ids, on_dif_referenced=None, return cficaches, dict((k, v) for k, v in conversion_errors.items()) return cficaches - def generate_caches(self, project, dif, fileobj=None): + def generate_caches(self, project, dif, filepath=None): """Generates a SymCache and CfiCache for the given debug information file if it supports these formats. Otherwise, a no - op. The caches are computed sequentially. @@ -673,26 +672,11 @@ def generate_caches(self, project, dif, fileobj=None): if not dif.supports_caches: return None - if fileobj is None: - fileobj = dif.file.getfile(as_tempfile=True) - close_fileobj = True - else: - fileobj.seek(0) - close_fileobj = False - - try: - _, _, error = self._update_cachefile(dif, fileobj, ProjectSymCacheFile) - if error is not None: - return error + if filepath: + return self._generate_caches_impl(dif, filepath) - _, _, error = self._update_cachefile(dif, fileobj, ProjectCfiCacheFile) - if error is not None: - return error - finally: - if close_fileobj: - fileobj.close() - - return None + with dif.file.getfile(as_tempfile=True) as tf: + return self._generate_caches_impl(dif, tf.name) def fetch_difs(self, project, debug_ids, features=None): """Given some ids returns an id to path mapping for where the @@ -714,6 +698,17 @@ def fetch_difs(self, project, debug_ids, features=None): return rv + def _generate_caches_impl(self, dif, filepath): + _, _, error = self._update_cachefile(dif, filepath, ProjectSymCacheFile) + if error is not None: + return error + + _, _, error = self._update_cachefile(dif, filepath, ProjectCfiCacheFile) + if error is not None: + return error + + return None + def _get_caches_impl(self, project, debug_ids, cls, on_dif_referenced=None): # Fetch debug files first and invoke the callback if we need debug_ids = [six.text_type(debug_id).lower() for debug_id in debug_ids] @@ -773,7 +768,7 @@ def _update_cachefiles(self, project, debug_files, cls): # a cache. This can either yield a cache object, an error or none of # the above. THE FILE DOWNLOAD CAN TAKE SIGNIFICANT TIME. with debug_file.file.getfile(as_tempfile=True) as tf: - file, cache, err = self._update_cachefile(debug_file, tf, cls) + file, cache, err = self._update_cachefile(debug_file, tf.name, cls) # Store this conversion error so that we can skip subsequent # conversions. There might be concurrent conversions running for the @@ -788,7 +783,7 @@ def _update_cachefiles(self, project, debug_files, cls): return rv, conversion_errors - def _update_cachefile(self, debug_file, fileobj, cls): + def _update_cachefile(self, debug_file, path, cls): debug_id = debug_file.debug_id # Skip silently if this cache cannot be computed from the given DIF @@ -799,7 +794,7 @@ def _update_cachefile(self, debug_file, fileobj, cls): # files by debug_id, we expect a corresponding object. Otherwise, we # fail silently, just like with missing symbols. try: - fo = FatObject.from_path(fileobj.name) + fo = FatObject.from_path(path) o = fo.get_object(id=debug_id) if o is None: return None, None, None diff --git a/src/sentry/tasks/assemble.py b/src/sentry/tasks/assemble.py index aa2328b21f0cab..81c5152a952857 100644 --- a/src/sentry/tasks/assemble.py +++ b/src/sentry/tasks/assemble.py @@ -64,7 +64,7 @@ def assemble_dif(project_id, name, checksum, chunks, **kwargs): # Try to generate caches from this DIF immediately. If this # fails, we can capture the error and report it to the uploader. # Also, we remove the file to prevent it from erroring again. - error = ProjectDebugFile.difcache.generate_caches(project, dif, temp_file) + error = ProjectDebugFile.difcache.generate_caches(project, dif, temp_file.name) if error is not None: set_assemble_status(project, checksum, ChunkFileState.ERROR, detail=error) From b41f910b9cc06c7cb791c5a648d88748680a13c5 Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Thu, 18 Oct 2018 19:33:14 +0200 Subject: [PATCH 08/10] test: Fix a test that patches symcache construction --- tests/sentry/lang/native/test_plugin.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/sentry/lang/native/test_plugin.py b/tests/sentry/lang/native/test_plugin.py index 02c82ecde8649f..4b2258af512076 100644 --- a/tests/sentry/lang/native/test_plugin.py +++ b/tests/sentry/lang/native/test_plugin.py @@ -14,7 +14,7 @@ from sentry.lang.native.symbolizer import Symbolizer from sentry.models import Event, EventAttachment, File, ProjectDebugFile -from symbolic import parse_addr, Object, SymbolicError +from symbolic import parse_addr, SymbolicError, SymCache class BasicResolvingIntegrationTest(TestCase): @@ -1147,11 +1147,13 @@ def test_broken_conversion(self): 'dSYM/hello') f.close() - original_make_symcache = Object.make_symcache + original_make_symcache = SymCache.from_object - def broken_make_symcache(self): + @classmethod + def broken_make_symcache(cls, obj): raise SymbolicError('shit on fire') - Object.make_symcache = broken_make_symcache + SymCache.from_object = broken_make_symcache + try: response = self.client.post( url, { @@ -1220,7 +1222,7 @@ def broken_make_symcache(self): } event.delete() finally: - Object.make_symcache = original_make_symcache + SymCache.from_object = original_make_symcache def test_debug_id_resolving(self): file = File.objects.create( From ad4c1641424369f737bc583ca4f7272f49e7929b Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Sun, 21 Oct 2018 17:50:08 +0200 Subject: [PATCH 09/10] test: Fix SQLite tests --- tests/sentry/models/test_debugfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/sentry/models/test_debugfile.py b/tests/sentry/models/test_debugfile.py index b509321559fca1..86a3918e218f7d 100644 --- a/tests/sentry/models/test_debugfile.py +++ b/tests/sentry/models/test_debugfile.py @@ -419,7 +419,7 @@ def test_update_symcache(self): assert debug_id in symcaches assert symcaches[debug_id].id == debug_id assert symcaches[debug_id].is_latest_file_format - assert not ProjectSymCacheFile.objects.filter(id=old_cache.id).exists() + assert not ProjectSymCacheFile.objects.filter(id=old_cache.id, version=1).exists() def test_get_symcache_on_referenced(self): debug_id = '67e9247c-814e-392b-a027-dbde6748fcbf' @@ -561,7 +561,7 @@ def test_update_cficache(self): cficaches = ProjectDebugFile.difcache.get_cficaches(self.project, [debug_id]) assert debug_id in cficaches assert cficaches[debug_id].is_latest_file_format - assert not ProjectCfiCacheFile.objects.filter(id=old_cache.id).exists() + assert not ProjectCfiCacheFile.objects.filter(id=old_cache.id, version=0).exists() def test_get_cficache_on_referenced(self): debug_id = '1ddb3423-950a-3646-b17b-d4360e6acfc9' From 651ae8b2630854a3b674a9be0b40ce56ecd7358c Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Mon, 22 Oct 2018 11:12:06 +0200 Subject: [PATCH 10/10] feat: Add a metric for failed debug cache conversions --- src/sentry/models/debugfile.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sentry/models/debugfile.py b/src/sentry/models/debugfile.py index 49d88778e7c9d0..703a112ae773eb 100644 --- a/src/sentry/models/debugfile.py +++ b/src/sentry/models/debugfile.py @@ -38,6 +38,7 @@ from sentry.models.file import File, ChunkFileState from sentry.reprocessing import resolve_processing_issue, \ bump_reprocessing_revision +from sentry.utils import metrics from sentry.utils.zip import safe_extract_zip from sentry.utils.decorators import classproperty @@ -805,6 +806,10 @@ def _update_cachefile(self, debug_file, path, cls): logger.error('dsymfile.%s-build-error' % cls.cache_name, exc_info=True, extra=dict(debug_id=debug_id)) + metrics.incr('%s.failed' % cls.cache_name, tags={ + 'error': e.__class__.__name__, + }) + return None, None, e.message file = File.objects.create(name=debug_id, type='project.%s' % cls.cache_name)