import hashlib from input.input_validator import generate_random_md5 # autogenerated class to hold the input data class InputObservation: """ A class to hold an input observation and associated metadata Attributes: image (ndarray): The image associated with the observation. latitude (float): The latitude where the observation was made. longitude (float): The longitude where the observation was made. author_email (str): The email of the author of the observation. date (Any): # FIXME: specify the type The date when the observation was made. time (Any): # FIXME: specify the type The time when the observation was made. date_option (datetime.date): Additional date option for the observation. time_option (datetime.time): Additional time option for the observation. uploaded_file (UploadedFile): The uploaded file associated with the observation. image_md5 (str): The MD5 hash of the image associated with the observation. Methods: __str__(): Returns a string representation of the observation. __repr__(): Returns a string representation of the observation. __eq__(other): Checks if two observations are equal. __ne__(other): Checks if two observations are not equal. show_diff(other): Shows the differences between two observations. to_dict(): Converts the observation to a dictionary. from_dict(data): Creates an observation from a dictionary. from_input(input): Creates an observation from another input observation. """ _inst_count = 0 def __init__( self, image:ndarray=None, latitude:float=None, longitude:float=None, author_email:str=None, date=None, time=None, date_option:datetime.date=None, time_option:datetime.time=None, uploaded_file:UploadedFile=None, image_md5:str=None): self.image = image self.latitude = latitude self.longitude = longitude self.author_email = author_email self.date = date self.time = time self.date_option = date_option self.time_option = time_option self.uploaded_file = uploaded_file self.image_md5 = image_md5 self._top_predictions = [] InputObservation._inst_count += 1 self._inst_id = InputObservation._inst_count #self.assign_image_md5() def set_top_predictions(self, top_predictions:list): self._top_predictions = top_predictions # add a method to get the top predictions (property?) @property def top_predictions(self): return self._top_predictions # add a method to assign the image_md5 only once def assign_image_md5(self): raise DeprecationWarning("This method is deprecated. hash is a required constructor argument.") if not self.image_md5: self.image_md5 = hashlib.md5(self.uploaded_file.read()).hexdigest() if self.uploaded_file else generate_random_md5() # new comment / hybj hunk self._cprint(f"[D] Assigned image md5: {self.image_md5} for {self.uploaded_file}") def _cprint(self, msg:str, color:str=OKGREEN): """Print colored message""" print(f"{color}{msg}{ENDC}") def __str__(self): _im_str = "None" if self.image is None else f"image dims: {self.image.shape}" return ( f"Observation: {_im_str}, {self.latitude}, {self.longitude}, " f"{self.author_email}, {self.date}, {self.time}, {self.date_option}, " f"{self.time_option}, {self.uploaded_file}, {self.image_md5}" ) def __repr__(self): _im_str = "None" if self.image is None else f"image dims: {self.image.shape}" return ( f"Observation: " f"Image: {_im_str}, " f"Latitude: {self.latitude}, " f"Longitude: {self.longitude}, " f"Author Email: {self.author_email}, " f"Date: {self.date}, " f"Time: {self.time}, " f"Date Option: {self.date_option}, " f"Time Option: {self.time_option}, " f"Uploaded Filename: {self.uploaded_file}" f"Image MD5 hash: {self.image_md5}" ) def __eq__(self, other): # TODO: ensure this covers all the attributes (some have been added?) # - except inst_id which is unique _image_equality = False if self.image is None or other.image is None: _image_equality = other.image == self.image else: # maybe strong assumption: both are correctly ndarray.. should I test types intead? _image_equality = (self.image == other.image).all() equality = ( #self.image == other.image and _image_equality and self.latitude == other.latitude and self.longitude == other.longitude and self.author_email == other.author_email and self.date == other.date and self.time == other.time and self.date_option == other.date_option and # temporarily skip time_option, it is followed by the clock and that is always differnt #self.time_option == other.time_option and self.uploaded_file == other.uploaded_file and self.image_md5 == other.image_md5 ) return equality # define a function show_diff(other) that shows the differences between two observations # only highlight the differences, if element is the same don't show it # have a summary at the top that shows if the observations are the same or not def show_diff(self, other): """Show the differences between two observations""" differences = [] if self.image is None or other.image is None: if other.image != self.image: differences.append(f" Image is different. (types mismatch: {type(self.image)} vs {type(other.image)})") else: if (self.image != other.image).any(): cnt = (self.image != other.image).sum() differences.append(f" Image is different: {cnt} different pixels.") if self.latitude != other.latitude: differences.append(f" Latitude is different. (self: {self.latitude}, other: {other.latitude})") if self.longitude != other.longitude: differences.append(f" Longitude is different. (self: {self.longitude}, other: {other.longitude})") if self.author_email != other.author_email: differences.append(f" Author email is different. (self: {self.author_email}, other: {other.author_email})") if self.date != other.date: differences.append(f" Date is different. (self: {self.date}, other: {other.date})") if self.time != other.time: differences.append(f" Time is different. (self: {self.time}, other: {other.time})") if self.date_option != other.date_option: differences.append(f" Date option is different. (self: {self.date_option}, other: {other.date_option})") if self.time_option != other.time_option: differences.append(f" Time option is different. (self: {self.time_option}, other: {other.time_option})") if self.uploaded_file != other.uploaded_file: differences.append(" Uploaded filename is different.") if self.image_md5 != other.image_md5: differences.append(" Image MD5 hash is different.") if differences: print(f"Observations have {len(differences)} differences:") for diff in differences: print(diff) else: print("Observations are the same.") def __ne__(self, other): return not self.__eq__(other) def to_dict(self): return { #"image": self.image, "image_filename": self.uploaded_file.name if self.uploaded_file else None, "image_md5": self.image_md5, #"image_md5": hashlib.md5(self.uploaded_file.read()).hexdigest() if self.uploaded_file else generate_random_md5(), "latitude": self.latitude, "longitude": self.longitude, "author_email": self.author_email, "date": self.date, "time": self.time, "date_option": str(self.date_option), "time_option": str(self.time_option), "uploaded_file": self.uploaded_file } @classmethod def from_dict(cls, data): return cls( image=data.get("image"), latitude=data.get("latitude"), longitude=data.get("longitude"), author_email=data.get("author_email"), date=data.get("date"), time=data.get("time"), date_option=data.get("date_option"), time_option=data.get("time_option"), uploaded_file=data.get("uploaded_file"), image_hash=data.get("image_md5") ) @classmethod def from_input(cls, input): return cls( image=input.image, latitude=input.latitude, longitude=input.longitude, author_email=input.author_email, date=input.date, time=input.time, date_option=input.date_option, time_option=input.time_option, uploaded_file=input.uploaded_file, image_hash=input.image_hash )