class VirtualPurchase(model.Model): id = ObjectIdField(unique=True, db_column="_id") user = model.ForeignKey(settings.AUTH_USER_MODEL, on_delete=model.CASCADE) asx_code = model.TextField(max_length=10) buy_date = model.DateField() price_at_buy_date = model.FloatField() amount = model.FloatField() # dollar value purchased (assumes no fees) n = ( model.IntegerField() ) # number of shares purchased at buy_date (rounded down to nearest whole share) objects = DjongoManager() def current_price(self): assert self.n > 0 quote, _ = latest_quote(self.asx_code) if quote is None: raise ValueError() buy_price = self.price_at_buy_date pct_move = (quote.last_price / buy_price) * 100.0 - 100.0 if buy_price > 0.0 else 0.0 return (self.n * quote.last_price, pct_move) def __str__(self): cur_price, pct_move = self.current_price() return "Purchase on {}: ${} ({} shares@${:.2f}) is now ${:.2f} ({:.2f}%)".format( self.buy_date, self.amount, self.n, self.price_at_buy_date, cur_price, pct_move ) class Meta: managed = True # viewer application db_table = "virtual_purchase"
class ECBMetadata(models.Model): """ Model responsible for describing allowable values for a given metadata_type (dimension, attribute, measure) which is used for form generation and validation """ id = ObjectIdField(db_column="_id", primary_key=True) metadata_type = models.TextField(db_column="metadata_type", null=False, blank=False) flow = models.TextField(db_column="flow_name", null=False, blank=False) code = models.TextField(db_column="item_name", null=False, blank=False) column_name = models.TextField(db_column="codelist_name", null=False, blank=False) printable_code = models.TextField(db_column="item_value", null=False, blank=False) objects = DjongoManager() class Meta: db_table = "ecb_metadata_index" verbose_name = "ECB Metadata" verbose_name_plural = "ECB Metadata"
class MarketQuoteCache(model.Model): # { "_id" : ObjectId("5f44c54457d4bb6dfe6b998f"), "scope" : "all-downloaded", # "tag" : "change_price-05-2020-asx", "dataframe_format" : "parquet", # "field" : "change_price", "last_updated" : ISODate("2020-08-25T08:01:08.804Z"), # "market" : "asx", "n_days" : 3, "n_stocks" : 0, # "sha256" : "75d0ad7e057621e6a73508a178615bcc436d97110bcc484f1cfb7d478475abc5", # "size_in_bytes" : 2939, "status" : "INCOMPLETE" } size_in_bytes = model.IntegerField() status = model.TextField() tag = model.TextField(db_index=True) dataframe_format = model.TextField() field = model.TextField() last_updated = model.DateTimeField() market = model.TextField() n_days = model.IntegerField() n_stocks = model.IntegerField() sha256 = model.TextField() _id = ObjectIdField() scope = model.TextField() dataframe = model.BinaryField() objects = DjongoManager() class Meta: managed = False # table is managed by persist_dataframes.py db_table = "market_quote_cache"
class BlacklistedDomain(m.Model): _id = ObjectIdField(db_column='_id') domain = m.CharField(blank=False, null=False, max_length=255) reason = m.CharField(blank=False, null=False, max_length=255) # responsibility for this model is not with Django so... class Meta: db_table = 'blacklisted_domains' managed = False
class Sector(model.Model): """ Table of ASX sector (GICS) names. Manually curated for now. """ id = ObjectIdField(unique=True, db_column="_id") sector_name = model.TextField(unique=True) sector_id = model.IntegerField(db_column='id') class Meta: managed = False db_table = "sector"
class CompanyFinancialMetric(model.Model): id = ObjectIdField(db_column="_id", primary_key=True) asx_code = model.TextField(blank=False, null=False) name = model.TextField(blank=False, null=False, db_column="metric") value = model.FloatField() as_at = model.DateTimeField(db_column="date") objects = DjongoManager() class Meta: db_table = "asx_company_financial_metrics"
class Script(m.Model): _id = ObjectIdField(db_column='_id') sha256 = m.CharField(blank=False, null=False, max_length=128) md5 = m.CharField(blank=False, null=False, max_length=64) size_bytes = m.IntegerField() code = m.BinaryField() # not editable by default # responsibility for this model is up to etl_upload_artefacts.py class Meta: db_table = "scripts" managed = False
class Comments(models.Model): _id = ObjectIdField(primary_key=True) body = models.TextField() questions = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='comments') def __str__(self): return 'Comment Id : {},Question : {}'.format(self._id, self.questions) class Meta: db_table = 'comments'
class Question(models.Model): _id = ObjectIdField(primary_key=True) title = models.TextField(max_length=1000) problem = models.TextField(max_length=100000) code = models.TextField(max_length=100000000, blank=True) expected = models.TextField(max_length=100000, blank=True) def __str__(self): return '_id :{}'.format(self._id) class Meta: db_table = 'questions'
class Watchlist(model.Model): id = ObjectIdField(unique=True, db_column="_id") # record stocks of interest to the user user = model.ForeignKey(settings.AUTH_USER_MODEL, on_delete=model.CASCADE) asx_code = model.TextField() when = model.DateTimeField(auto_now_add=True) objects = DjongoManager() class Meta: managed = True # viewer application is responsible NOT asxtrade.py db_table = "user_watchlist"
class JavascriptControlSummary(m.Model): _id = ObjectIdField(db_column='_id') origin = m.CharField( blank=False, null=False, max_length=255 ) # lookup into JavascriptControl model, although do_not_load=True may be in effect last_updated = m.CharField(blank=False, null=False, max_length=64) sum_of_ast_features = m.IntegerField() sum_of_functions = m.IntegerField() sum_of_literals = m.IntegerField() # responsibility for this model is up to etl_control_upload*.py so... class Meta: db_table = "javascript_controls_summary" managed = False
class WorldBankIndicators(model.Model): id = ObjectIdField(primary_key=True, db_column="_id") wb_id = model.TextField( ) # world bank id, not to be confused with django id/pk name = model.TextField() last_updated = model.DateTimeField(auto_now_add=True) unit = model.TextField() source = JSONField() source_note = model.TextField() topics = ArrayField(WorldBankTopic) source_organisation = model.TextField() last_successful_data = model.DateTimeField(null=True) last_error_when = model.DateTimeField( null=True ) # date of last ingest error for this indicator (or None if not known) last_error_msg = model.TextField() # eg. Error code 175 last_error_type = model.TextField() # eg. class RuntimeError objects = DjongoManager() def __init__(self, *args, **kwargs): self.wb_id = kwargs.pop("id", None) self.source_organisation = kwargs.pop("sourceOrganization", None) self.source_note = kwargs.pop("sourceNote", None) # wb_id=wb_id, source_organisation=source_organisation, source_note=source_note super(WorldBankIndicators, self).__init__(*args, **kwargs) def __str__(self): return f"{self.wb_id} {self.name} {self.source} last_error_when={self.last_error_when} last_updated={self.last_updated}" @property def tag(self ): # NB: must match the ingest_worldbank_datasets.py tag name... return f"{self.wb_id}-yearly-dataframe" @property def has_data(self): obj = WorldBankDataCache.objects.filter(tag=self.tag).first() return obj is not None def fetch_data(self) -> pd.DataFrame: t = self.tag print(f"Fetching parquet dataframe for {t}") return get_parquet(t) class Meta: db_table = "world_bank_indicators" verbose_name = "World Bank Metric" verbose_name_plural = "World Bank Metrics"
class WorldBankCountry(model.Model): """ Not strictly a country, can include regions eg. africa ex-warzones """ id = ObjectIdField(primary_key=True, db_column="_id") country_code = model.TextField() name = model.TextField() last_updated = model.DateTimeField(auto_now_add=True) objects = DjongoManager() class Meta: db_table = "world_bank_countries" verbose_name_plural = "World Bank Countries"
class Security(model.Model): # eg. { "_id" : ObjectId("5efe83dd4b1fe020d5ba2de8"), "asx_code" : "T3DAC", # "asx_isin_code" : "AU0000T3DAC0", "company_name" : "333D LIMITED", # "last_updated" : ISODate("2020-07-26T00:49:11.052Z"), # "security_name" : "OPTION EXPIRING 18-AUG-2018 RESTRICTED" } _id = ObjectIdField() asx_code = model.TextField(blank=False, null=False) asx_isin_code = model.TextField() company_name = model.TextField() last_updated = model.DateField() security_name = model.TextField() class Meta: db_table = "asx_isin" managed = False # managed by asxtrade.py
class ABSInvertedIndex(model.Model): id = ObjectIdField(primary_key=True, db_column="_id") min_date = model.DateTimeField() max_date = model.DateTimeField() n_attributes = model.IntegerField() last_updated = model.DateTimeField(auto_now_add=True) tag = model.TextField() dataset = model.TextField() name = model.TextField() scope = model.TextField() # always 'abs' for now objects = DjongoManager() class Meta: db_table = "abs_inverted_index"
class JavascriptControl(m.Model): _id = ObjectIdField(db_column='_id') family = m.CharField(blank=False, null=False, max_length=100) origin = m.CharField(blank=False, null=False, max_length=1024) do_not_load = m.BooleanField() size_bytes = m.IntegerField() variant = m.CharField(max_length=100) release = m.CharField(max_length=100) content_type = m.CharField(max_length=100) provider = m.CharField(max_length=100) subfamily = m.CharField(max_length=255) # responsibility for this model is up to etl_control_upload*.py so... class Meta: db_table = 'javascript_controls' managed = False
class WorldBankInvertedIndex(model.Model): id = ObjectIdField(primary_key=True, db_column="_id") country = model.TextField() topic_id = model.IntegerField() topic_name = model.TextField() n_attributes = model.IntegerField() last_updated = model.DateTimeField() tag = model.TextField() indicator_id = model.TextField() # xref into WorldBankIndicators.wb_id indicator_name = model.TextField() # likewise to name field objects = DjongoManager() class Meta: db_table = "world_bank_inverted_index" verbose_name_plural = "World Bank Inverted Indexes"
class CommitSerializer(serializers.ModelSerializer): _id = ObjectIdField() modifiedFiles = serializers.SerializerMethodField() user = CharField(max_length=128) hashID = CharField(max_length=40) email = EmailField() date = DateTimeField() message = CharField(max_length=510) class Meta: model = Commit exclude = ('_id', ) def get_modifiedFiles(self, obj): resources_serializer = ModifiedFilesSerializer(obj.modifiedFiles, many=True) return resources_serializer.data
class FeaturesModel(m.Model): # { "_id" : ObjectId("5ee1f3b8a4b98699c0917c8f"), "js_id" : "5e3cff0baf4b1411a1429ff4", "byte_content_sha256" : "bae214050996634a9a61dae7e12a3afd3fed131db5d90a14abfa66b49dcf7eea", "content_type" : "text/javascript", "inline" : false, "last_updated" : ISODate("2020-06-22T06:37:34.977Z"), "md5" : "2882cbfe23dc5802cf598e7a20409304", "origin" : "https://clubs.canberra.edu.au/Account/Register", "sha256" : "ff0ada03194dbd93b98c88e54b6e1637c2b4b4458907401f869187d90efd08ba", "size_bytes" : 23828, "url" : "https://clubs.canberra.edu.au/Scripts/expressive.annotations.validate.js", "when" : "2020-06-21 22:42:10.323637" } _id = ObjectIdField(db_column='_id') js_id = m.CharField(max_length=24) content_type = m.CharField(max_length=100) inline = m.BooleanField() last_updated = m.DateTimeField() md5 = m.CharField(blank=False, null=False, max_length=64) origin = m.CharField(max_length=1024) sha256 = m.CharField(blank=False, null=False, max_length=128) size_bytes = m.IntegerField() url = m.CharField(max_length=1024) when = m.CharField(max_length=64) analysis_bytes = m.BinaryField() class Meta: db_table = "analysis_content" managed = False
class Vehicles(models.Model): _id = ObjectIdField() estimated_price = models.IntegerField() car_name = models.CharField(max_length=100) miles = models.CharField(max_length=500) vin = models.CharField(max_length=500) body = models.CharField(max_length=500) primary_damage = models.CharField(max_length=500) secondary_damage = models.CharField(max_length=500) start_code = models.CharField(max_length=500) key_fob = models.CharField(max_length=500) airbags = models.CharField(max_length=500) car_image = models.CharField(max_length=500) url = models.CharField(max_length=500) value_est = models.FloatField() class Meta: db_table = "scrapy_items"
class VetAgainstControl(m.Model): # unlike ControlHit which is the "best" hit for a given control-origin pair, this is a point-in-time datapoint # { "_id" : ObjectId("5ecb5cbed7161ef4963d4498"), "origin_url" : "https://www.playfootball.com.au/sites/play/files/js/js_BaupD1b1RyIB49fG7a7PVMg8ZvbSJmHIlTWnZmKr9L8.js", "ast_dist" : Infinity, "cited_on" : "https://www.playfootball.com.au/?id=172", "control_url" : "", "diff_functions" : "", "diff_literals" : "", "function_dist" : Infinity, "literal_dist" : 0, "literals_not_in_control" : -1, "literals_not_in_origin" : -1, "n_diff_literals" : -1, "origin_js_id" : "5e8da771af53ffdf7ee0b88b", "sha256_matched" : false, "xref" : null, "origin_vectors_sha256" : "8edf3e27259d16c1430b28ee2ad609c0ebda25626d089f4fcc35e6e6911bee0c" } _id = ObjectIdField(db_column='_id') control_url = m.CharField(max_length=1024) origin_url = m.CharField(max_length=1024) ast_dist = m.FloatField() function_dist = m.FloatField() literal_dist = m.FloatField() literals_not_in_control = m.IntegerField() literals_not_in_origin = m.IntegerField() n_diff_literals = m.IntegerField() origin_js_id = m.CharField(max_length=24) xref = m.CharField(max_length=24) sha256_matched = m.BooleanField() class Meta: db_table = "vet_against_control" managed = False
class ImageCache(model.Model): # some images in viewer app are expensive to compute, so we cache them # and if less than a week old, use them rather than recompute. The views # automatically check the cache for expensive images eg. sector performance plot _id = ObjectIdField() base64 = model.TextField(max_length=400 * 1024, null=False, blank=False) tag = model.TextField( max_length=100, null=False, blank=False) # used to identify if image is in ImageCache last_updated = model.DateTimeField(auto_now_add=True) valid_until = model.DateTimeField( auto_now_add=True ) # image is cached until is_outdated(), up to caller to set this value correctly def is_outdated(self): return self.last_updated > self.valid_until class Meta: managed = True db_table = "image_cache"
class ECBDataCache(models.Model): """ Similar to worldbank data cache """ id = ObjectIdField(db_column="_id", primary_key=True) size_in_bytes = models.IntegerField() status = models.TextField() tag = models.TextField() dataframe_format = models.TextField() field = models.TextField() last_updated = models.DateTimeField() market = models.TextField() n_days = models.IntegerField() n_stocks = models.IntegerField() sha256 = models.TextField() scope = models.TextField() dataframe = models.BinaryField() objects = DjongoManager() class Meta: db_table = "ecb_data_cache" verbose_name = "ECB Data Cache" verbose_name_plural = "ECB Data Cache"
class ECBFlow(models.Model): """ Describe each dataflow ingested from ECB SDMX REST API """ id = ObjectIdField(db_column="_id", primary_key=True) name = models.TextField(db_column="flow_name", null=False, blank=False) description = models.TextField(db_column="flow_descr", null=False, blank=False) is_test_data = models.BooleanField() last_updated = models.DateTimeField() prepared = models.DateTimeField() sender = models.TextField() source = models.TextField() data_available = models.BooleanField() objects = DjongoManager() class Meta: db_table = "ecb_flow_index" verbose_name = "ECB Flow" verbose_name_plural = "ECB Flow"
class ControlHit(m.Model): _id = ObjectIdField(db_column='_id') control_url = m.CharField(blank=False, null=False, max_length=1024) origin_url = m.CharField(blank=False, null=False, max_length=1024) sha256_matched = m.BooleanField() ast_dist = m.FloatField() function_dist = m.FloatField() literal_dist = m.FloatField() cited_on = m.CharField(blank=False, null=False, max_length=1024) #origin_js_id = m.ForeignKey(Script, on_delete=m.DO_NOTHING) #xref = m.ForeignKey(VetAgainstControl, on_delete=m.DO_NOTHING) origin_js_id = m.CharField(blank=False, null=False, max_length=24) xref = m.CharField(blank=False, null=False, max_length=24) literals_not_in_control = m.IntegerField() literals_not_in_origin = m.IntegerField() n_diff_literals = m.IntegerField() diff_literals = m.CharField(null=False, max_length=1000000) origin_host = m.CharField(max_length=255) origin_has_query = m.BooleanField() origin_port = m.IntegerField() origin_scheme = m.CharField(max_length=10) origin_path = m.CharField(max_length=1024) cited_on_host = m.CharField(max_length=255) cited_on_has_query = m.BooleanField() cited_on_port = m.IntegerField() cited_on_scheme = m.CharField(max_length=10) cited_on_path = m.CharField(max_length=1024) control_family = m.CharField(max_length=255) diff_functions = JSONField() # permit direct access to mongo for convenient API objects = DjongoManager() # responsibility for this data is left entirely to etl_publish_hits.py class Meta: db_table = "etl_hits" managed = False
class ABSDataCache(model.Model): """ Similar to, but separate from, app.MarketQuoteCache, this keeps track of pandas dataframes (parquet format) which have been downloaded, cleaned and ingested into MongoDB """ size_in_bytes = model.IntegerField() status = model.TextField() tag = model.TextField() dataframe_format = model.TextField() field = model.TextField() last_updated = model.DateTimeField() market = model.TextField() n_days = model.IntegerField() n_stocks = model.IntegerField() sha256 = model.TextField() _id = ObjectIdField() scope = model.TextField() dataframe = model.BinaryField() objects = DjongoManager() class Meta: db_table = "abs_data_cache"
class Quotation(model.Model): _id = ObjectIdField() error_code = model.TextField( max_length=100) # ignore record iff set to non-empty error_descr = model.TextField(max_length=100) fetch_date = model.TextField( null=False, blank=False) # used as an index (string) always YYYY-mm-dd asx_code = model.TextField(blank=False, null=False, max_length=20) annual_dividend_yield = model.FloatField() average_daily_volume = model.IntegerField() bid_price = model.FloatField() change_in_percent = model.FloatField() change_price = model.FloatField() code = model.TextField(blank=False, null=False, max_length=20) day_high_price = model.FloatField() day_low_price = model.FloatField() deprecated_market_cap = (model.IntegerField() ) # NB: deprecated use market_cap instead deprecated_number_of_shares = model.IntegerField() descr_full = model.TextField(max_length=100) # eg. Ordinary Fully Paid eps = model.FloatField() isin_code = model.TextField(max_length=100) last_price = model.FloatField() last_trade_date = model.DateField() market_cap = model.IntegerField() number_of_shares = model.IntegerField() offer_price = model.FloatField() open_price = model.FloatField() pe = model.FloatField() previous_close_price = model.FloatField() previous_day_percentage_change = model.FloatField() suspended = model.BooleanField(default=False) volume = model.IntegerField() year_high_date = model.DateField() year_high_price = model.FloatField() year_low_date = model.DateField() year_low_price = model.FloatField() objects = DjongoManager() # convenient access to mongo API def __str__(self): assert self is not None return str(model_to_dict(self)) def is_error(self): if self.error_code is None: return False return len(self.error_code) > 0 def eps_as_cents(self): if any([self.is_error(), self.eps is None]): return 0.0 return self.eps * 100.0 def volume_as_millions(self): """ Return the volume as a formatted string (rounded to 2 decimal place) represent the millions of dollars transacted for a given quote """ if any([self.is_error(), self.volume is None, self.last_price is None]): return "" return "{:.2f}".format(self.volume * self.last_price / 1000000.0) class Meta: db_table = "asx_prices" managed = False # managed by asxtrade.py indexes = [ model.Index(fields=["asx_code"]), model.Index(fields=["fetch_date"]) ]
class CompanyDetails(model.Model): # { "_id" : ObjectId("5eff01d14b1fe020d5453e8f"), "asx_code" : "NIC", "delisting_date" : null, # "fax_number" : "02 9221 6333", "fiscal_year_end" : "31/12", "foreign_exempt" : false, # "industry_group_name" : "Materials", # "latest_annual_reports" : [ { "id" : "02229616", "document_release_date" : "2020-04-29T14:45:12+1000", # "document_date" : "2020-04-29T14:39:36+1000", "url" : "http://www.asx.com.au/asxpdf/20200429/pdf/44hc5731pmh9mw.pdf", "relative_url" : "/asxpdf/20200429/pdf/44hc5731pmh9mw.pdf", "header" : "Annual Report and Notice of AGM", "market_sensitive" : false, "number_of_pages" : 118, "size" : "4.0MB", "legacy_announcement" : false }, { "id" : "02209126", "document_release_date" : "2020-02-28T18:09:26+1100", "document_date" : "2020-02-28T18:06:25+1100", "url" : "http://www.asx.com.au/asxpdf/20200228/pdf/44fm8tp5qy0k7x.pdf", "relative_url" : "/asxpdf/20200228/pdf/44fm8tp5qy0k7x.pdf", # "header" : "Annual Report and Appendix 4E", "market_sensitive" : true, "number_of_pages" : 64, # "size" : "1.6MB", "legacy_announcement" : false }, { "id" : "02163933", "document_release_date" : # "2019-10-25T11:50:50+1100", "document_date" : "2019-10-25T11:48:43+1100", # "url" : "http://www.asx.com.au/asxpdf/20191025/pdf/449w6d0phvgr05.pdf", # "relative_url" : "/asxpdf/20191025/pdf/449w6d0phvgr05.pdf", "header" : "Annual Report and Notice of AGM", # "market_sensitive" : false, "number_of_pages" : 74, "size" : "2.5MB", "legacy_announcement" : false } ], # "listing_date" : "2018-08-20T00:00:00+1000", # "mailing_address" : "Level 2, 66 Hunter Street, SYDNEY, NSW, AUSTRALIA, 2000", # "name_abbrev" : "NICKELMINESLIMITED", "name_full" : "NICKEL MINES LIMITED", # "name_short" : "NICKLMINES", "phone_number" : "02 9300 3311", # "primary_share" : { "code" : "NIC", "isin_code" : "AU0000018236", "desc_full" : "Ordinary Fully Paid", # "last_price" : 0.61, "open_price" : 0.595, "day_high_price" : 0.615, "day_low_price" : 0.585, # "change_price" : 0.02, "change_in_percent" : "3.39%", "volume" : 3127893, "bid_price" : 0.605, # "offer_price" : 0.61, "previous_close_price" : 0.59, "previous_day_percentage_change" : "1.724%", # "year_high_price" : 0.731, "last_trade_date" : "2020-07-24T00:00:00+1000", # "year_high_date" : "2019-09-24T00:00:00+1000", "year_low_price" : 0.293, # "year_low_date" : "2020-03-18T00:00:00+1100", "pe" : 8.73, "eps" : 0.0699, # "average_daily_volume" : 7504062, "annual_dividend_yield" : 0, "market_cap" : 1255578789, # "number_of_shares" : 2128099642, "deprecated_market_cap" : 1127131000, # "deprecated_number_of_shares" : 1847755410, "suspended" : false, # "indices" : [ { "index_code" : "XKO", "name_full" : "S&P/ASX 300", "name_short" : "S&P/ASX300", # "name_abrev" : "S&P/ASX 300" }, { "index_code" : "XAO", "name_full" : "ALL ORDINARIES", # "name_short" : "ALL ORDS", "name_abrev" : "All Ordinaries" }, { "index_code" : "XSO", # "name_full" : "S&P/ASX SMALL ORDINARIES", "name_short" : "Small Ords", # "name_abrev" : "S&P/ASX Small Ords" }, { "index_code" : "XMM", # "name_full" : "S&P/ASX 300 Metals and Mining (Industry)", "name_short" : "MTL&MINING", # "name_abrev" : "Metals and Mining" } ] }, "primary_share_code" : "NIC", # "principal_activities" : "Nickel pig iron and nickel ore production.", # "products" : [ "shares" ], "recent_announcement" : false, # "registry_address" : "Level 3, 60 Carrington Street, SYDNEY, NSW, AUSTRALIA, 2000", # "registry_name" : "COMPUTERSHARE INVESTOR SERVICES PTY LIMITED", # "registry_phone_number" : "02 8234 5000", "sector_name" : "Metals & Mining", # "web_address" : "http://www.nickelmines.com.au/" } _id = ObjectIdField() asx_code = model.TextField(null=False, blank=False) delisting_date = model.TextField(null=True) # if null, not delisted name_full = model.TextField(null=False, blank=False) phone_number = model.TextField(null=False, blank=True) bid_price = model.FloatField() offer_price = model.FloatField() latest_annual_reports = JSONField() previous_close_price = model.FloatField() average_daily_volume = model.IntegerField() number_of_shares = model.IntegerField() suspended = model.BooleanField() indices = JSONField() primary_share_code = model.TextField() principal_activities = model.TextField() products = JSONField() recent_announcement = model.BooleanField() registry_address = model.TextField() registry_name = model.TextField() registry_phone_number = model.TextField() sector_name = model.TextField() web_address = model.TextField() objects = DjongoManager() class Meta: managed = False db_table = "asx_company_details" verbose_name = "Company Detail" verbose_name_plural = "Company Details"