text: from django.core.exceptions import ObjectDoesNotExist
  from django.utils.dateparse import parse_datetime
  from django.db import transaction
  from django.conf import settings
  
  from api.models import Asteroid, AsteroidSighting, SightingFile
  
  import numpy as np
  import csv, pytz
  
  class SightingsFileRow:
      """
      Class for analyzing each asteroid sighting included in a sightings tsv file
      """
  
      def __init__(self, entries):
          self.__dict__.update(entries)
          self.asteroid_matrix = []
          self.asteroid_matrix_string = ''
          self.asteroid_resolution = ''
  
      def set_asteroid_data(self):
          """
          Identifies and isolates the asteroid from the sighting matrix
          """
  
          [_, height] = self.device_resolution.split('x')
  
          matrix = np.split(np.array(list(self.device_matrix)), int(height))
          clip_coord = {'top': len(matrix), 'right': 0, 'bottom': 0, 'left': len(matrix[0])}
  
          for y, row in enumerate(matrix):
              for x, cell in enumerate(row):
                  if (cell == '1'):
                      if ((y == 0 or matrix[y - 1][x] == '0') and clip_coord['top'] > y):
                          clip_coord['top'] = y
                      if ((x == len(row) - 1 or row[x + 1] == '0') and clip_coord['right'] < x):
                          clip_coord['right'] = x
                      if ((y == len(matrix) - 1 or matrix[y + 1][x] == '0') and clip_coord['bottom'] < y):
                          clip_coord['bottom'] = y
                      if ((x == 0 or row[x - 1] == '0') and clip_coord['left'] > x):
                          clip_coord['left'] = x
  
          self.asteroid_matrix = np.array(matrix)[clip_coord['top']:clip_coord['bottom'] + 1, clip_coord['left']:clip_coord['right'] + 1]
          self.asteroid_matrix_string = ''.join(np.hstack(self.asteroid_matrix))
          self.asteroid_resolution = '{0}x{1}'.format(self.asteroid_matrix.shape[1], self.asteroid_matrix.shape[0])
      
      def check_asteroid_in_db(self):
          """
          Checks if the sighted asteroid is included in the database
          """
  
          index = None
          try:
              asteroid = Asteroid.objects.get(matrix__exact=self.asteroid_matrix_string)
              index = asteroid.id 
          except ObjectDoesNotExist as e:
              index = -1
  
          return index
  
      @transaction.atomic
      def store(self):
          """
          Stores the data related to the asteroid sighting and, if the asteroid was not in the database,
          stores it as well.
          """
  
          self.set_asteroid_data()
          asteroid_id = self.check_asteroid_in_db()
  
          if (asteroid_id == -1):
              asteroid = Asteroid(
                  resolution=self.asteroid_resolution,
                  matrix=self.asteroid_matrix_string
              )
              asteroid.save()
              print('New asteroid stored: {}'.format(str(asteroid)))
          else:
              asteroid = Asteroid.objects.get(pk=asteroid_id)
  
          date = parse_datetime('{0}T{1}'.format(self.date, self.time))
          tz_aware_date = date.replace(tzinfo=pytz.timezone(settings.TIME_ZONE))
  
          sighting = AsteroidSighting(
              asteroid=asteroid,
              date=tz_aware_date,
              observatory_code=self.observatory_code,
              device_code=self.device_code,
              device_resolution=self.device_resolution,
              device_matrix=self.device_matrix
          )
          sighting.save()
  
          print('New Sighting stored: {}'.format(str(sighting)))
  
  
  class SightingsFileParser:
      """
      Class with methods to parse and store the data of an asteroid sighting
      included in a tsv file
      """
  
      def __init__(self, file_path):
          self.file_path = file_path
          self.rows = []
  
      def parse(self):
          """
          Parses the tsv data        
          """
  
          with open(self.file_path, 'r') as f:
              contents = csv.DictReader(f, delimiter="\t")
              for row in contents:
                  self.rows.append(SightingsFileRow(row))
  
      @transaction.atomic
      def store_sightings(self):
          """
          Stores the asteroid sightings data included in the tsv file
          """
  
          for row in self.rows:
              row.store()
          
          sighting_file = SightingFile(name=self.file_path.name)
          sighting_file.save()
  