import django.db.utils from django.core.management.base import BaseCommand from django.apps import apps from django.db.utils import IntegrityError from app.models import * import csv import json class Command(BaseCommand): help = 'Imports csv to DB' csv_files_models_dict = { "v2-LU_GMBA_SpeciesGroups.csv": "GMBA_SpeciesGroup", "v2-LU_Countries.csv": "Country", "v2-LU_Languages.csv": "Language", "v2-LU_Sources.csv": "Source", "v2-LU_RedListCategories.csv": "RedListCategory", "v2-LU_RangeTypes.csv": "RangeType", "v2-LU_PeopleStatus.csv": "PeopleStatus", "v2-LU_TrendsQuantity.csv": "TrendsQuantity", "v2-LU_TrendsQuality.csv": "TrendsQuality", "v2-LU_TaxonUnit.csv": "TaxonUnit", "v2-LU_TaxonStatus.csv": "TaxonStatus", "v2-Ranges-cleaned.csv": "MountainRange", "v2-AddElevations.csv": "AddElevation", "v2-GMBA_Function.csv": "GMBA_function", "v2-Gmba_V2_centroid.csv": "GMBA_V2_Centroid", "v2-ImportGeom210915.csv": "ImportGeom210915", "v2-LanguageLink.csv": "LanguageLink", "v2-Keywords.csv": "Keyword", "v2-NamesImport.csv": "NamesImport", "v2-Organisations-cleaned.csv": "Organisation", "v2-Peaks.csv": "Peak", "v2-People.csv": "Person", "v2-PeopleRanges.csv": "PeopleRange", "v2-PeopleFunction.csv": "PeopleFunction", "v2-Resources.csv": "Resource", "v2-PeopleResources.csv": "PeopleResource", "v2-RangeCountries.csv": "RangeCountry", "v2-RangeNameTranslations.csv": "RangeNameTranslation", "v2-RangeOnlineInfo.csv": "RangeOnlineInfo", "v2-ResourceRanges.csv": "ResourceRange", "v2-ResourceKeywords.csv": "ResourceKeyword", "v2-Repositories.csv": "Repository", "v2-Species.csv": "Species", "v2-Searches.csv": "Search", "v2-TaxonRange.csv": "TaxonRange", "v2-SpeciesRange.csv": "SpeciesRange" } cols_to_django_fields = { "ID": 'id', "Source": 'source', "RangeName": 'range_name_id', "LanguageTranslation": 'language_translation_id', "RangeNameTranslation": 'range_name_translation', "GMBA_ID_v2": 'gmba_v2_id', "Elev_Min": 'elev_min', "Elev_Max": 'elev_max', "Elev_Range": 'elev_range', "TaxonStatus": 'taxon_status', "InfoSource": 'info_source', "URL": 'url', "GMBA function": 'gmba_function', "TaxonUnit": 'taxon_unit', "Range_ID": 'id', "RangeNameMap": 'range_name_map', "RangeNameAscii": 'range_name_ascii', "RangeNameLanguage": 'range_name_language', "MotherRange": 'mother_range', "Feature": 'feature', "MapUnit": 'map_unit', "Level": 'level', "LevelText": 'level_text', "Level_1": 'level_1', "Level_2": 'level_2', "Level_3": 'level_3', "Latitude": 'latitude', "Longitude": 'longitude', "Orogeny": 'orogeny', "Area": 'area', "GMBA_V1_ID": 'GMBA_v1_id', "Countries": 'countries', "Peak_Elevation": 'peak_elevation', "Peak_Name": 'peak_name', "Peak_Latitude": 'peak_latitude', "Peak_Longitude": 'peak_longitude', "Comments": 'comments', "Checked": 'checked', "Range_AlternateID": 'range_alternate_id', "GeologicRegion": 'geologic_region', "GMBA_V2_ID": 'gmba_v2_id', "GMBA_V2_ID_str": 'gmba_v2_id_str', "WikiDataID": 'wiki_data_id', "WikiDataURL": 'wiki_data_url', "Select_300": 'select_300', "Gmba_Narrow": 'gmba_narrow', "Name_FR": 'name_fr', "Name_DE": 'name_de', "Name_ES": 'name_es', "Name_PT": 'name_pt', "Name_CN": 'name_cn', "Name_RU": 'name_ru', "Name_TR": 'name_tr', "Perimeter": 'perimeter', "ColorAll": 'color_all', "ColorBasic": 'color_basic', "Color300": 'color_300', "Elev_Low": 'elev_low', "Elev_High": 'elev_high', "Elev_Avg": 'elev_avg', "gridcode": 'gridcode', "Trend": 'trend', "RepositoryName": 'repository_name', "RepositoryURL": 'repository_url', "Resource": 'resource_id', "Keyword": 'keyword', "Keyword_ID": 'keyword_id', "Mother": 'mother', "CN": 'cn', "DE": 'de', "ES": 'es', "FR": 'fr', "PT": 'pt', "RU": 'ru', "TR": 'tr', "ResourceTitle": 'resource_title_id', "LanguageLetterCode": 'language_letter_code', "LanguageNumberCode": 'language_number_code_id', "OrgNum1": 'org_num1', "Organisation Search": 'organisation_search', "OrgAlphaSearch": 'org_alpha_search', "Organisation English": 'organisation_english', "Organisation 2": 'organisation_2', "Organisation 3": 'organisation_3', "Organisation Original": 'organisation_original', "Acronym": 'acronym', "Street": 'street', "PO Box": 'po_box', "Postcode": 'postcode', "City": 'city', "Region": 'region', "SearchURL": 'search_url', "LatLon": 'lat_long', "URL Org": 'url', "Tel Org": 'tel', "Email Org": 'email', "Country": 'country_id', "Tags": 'tags', "Description": 'description', "Northing": 'northing', "Easting": 'easting', "Category": 'category', "Subject": 'subject', "Title": 'title', "Citation": 'citation', "Type": 'type', "Abstract": 'abstract', "AuthorKeywords": 'author_keywords', "Lat": 'lat', "Lon": 'lon', "Stars": 'stars', "PEGASuS_Check_map_with_author": 'PEGASuS_Check_map_with_author', "PEGASuS_polygon_ID": 'PEGASuS_polygon_ID', "PEGASuS_Polygon_comments": 'PEGASuS_Polygon_comments', "PEGASuS_Assessment_ID": 'PEGASuS_Assessment_ID', "GLORIA": 'gloria', "GNOMO": 'gnomo', "LTER": 'lter', "LTSER": 'ltser', "MIREN": 'miren', "TEAM": 'team', "Inventory": 'inventory', "DOI": 'doi', "ShortName": 'short_name', "FormalName": 'formal_name', "Membership within the UN System": 'membership_within_un_system', "Membership within theĀ UN System": 'membership_within_un_system', "Continent": 'continent', "EU_MS": 'eu_ms', "EEA_MS": 'eea_ms', "ISO3": 'iso3', "ISO2": 'iso2', "Point_Name": 'point_name', "Elevation": 'elevation', "Link": 'link', "Repository": 'repository_id', "SearchString": 'search_string', "SearchDate": 'search_date', "Result": 'result', "NumberOfRecords": 'number_of_records', "Stored": 'stored', "SpeciesGroup": 'species_group', "MrMrs": 'mr_mrs', "First name": 'first_name', "Last name": 'last_name', "Full name": 'full_name', "SearchName": 'search_name', "e-mail 1": 'contact_email', "e-mail 2": 'email_2', "Skype": 'skype', "Professional phone": 'professional_phone', "Mobile number": 'mobile_number', "Field of expertise": 'field_of_expertise', "Biography": 'biography', "Position": 'position', "Status": 'status', "Entry date": 'entry_date', "Newsletter": 'news_letter', "CountryLookup": 'country_lookup', "Organisation": 'organization_id', "Birds": 'birds', "Mammals": 'mammals', 'Reptiles': 'reptiles', 'Amphibians': 'amphibians', 'Fish': 'fish', 'Insects': 'insects', 'Molluscs': 'molluscs', 'Crustaceans': 'crustaceans', 'Arachnids': 'arachnids', 'Angiosperms': 'angiosperms', 'Gymnosperms': 'gymnosperms', 'Fungi': 'fungi', 'Algae': 'algae', 'Microbes': 'microbes', 'Biological field sampling': 'biological_field_sampling', 'Data mining': 'data_mining', 'Remote sensing': 'remote_sensing', 'GIS': 'gis', 'Spatial analysis': 'spatial_analysis', 'Statistical analysis': 'statistical_analysis', 'Modelling': 'modelling', 'Assessment': 'assessment', 'Meta-analysis': 'meta_analysis', 'Synthesis': 'synthesis', 'Qualitative social science methods (interviews, surveys)': 'qualitative_ssm', 'Genetic analyses': 'genetic_analyses', 'Field site': 'field_site', 'Transect': 'transect', 'Mountain top': 'mountain_top', 'Mountain range': 'mountain_range', 'Landscape': 'landscape', 'Regional': 'regional', 'National': 'national', 'Global': '_global', 'Geographic area of expertise': 'geographic_area_of_expertise', 'ProfileOnWeb': 'profile_on_web', 'Updated': 'updated', 'ORCID': 'orcid', 'WebOfScience': 'web_of_science', 'Twitter': 'twitter', 'Instagram': 'instagram', 'ScientificName': 'scientific_name_id', 'Class': '_class', 'EnglishName': 'english_name', 'Language': 'language', 'Person': 'person_id', 'Field': 'field_id', 'Method': 'method_id', 'Scale': 'scale_id', 'Function': 'function_id', 'Range': 'range_id', 'Endemic': 'endemic', 'SourceURL': 'source_url', 'MountainRange': 'mountain_range', 'TaxonRangeID': 'id', 'SubRangeOrRegion': 'subrange_or_region', 'Taxon': 'taxon_id', 'Distribution': 'distribution', 'RedList': 'redlist', 'CountUnit': 'count_unit', 'NumberUnits': 'number_of_units', 'Remarks': 'remarks', 'RangeType': 'range_type', 'Role': 'role', 'RedListCategory': 'red_list_category' } def add_arguments(self, parser): parser.add_argument('--path', type=str, help="file path") parser.add_argument('--csv_folder_path', type=str, help="Path where the csvs are located") parser.add_argument('--model_name', type=str, help="model name") parser.add_argument('--app_name', type=str, help="django app name that the model is connected to", default='app') parser.add_argument('--mother_range_reload', action='store_true', help="Whether we are trying to reload mother " "range relationship") parser.add_argument('--debug', action='store_true', help="Whether we want to debug") parser.add_argument('--all', action='store_true', help="Imports all csvs") # ./manage.py import --path /home/pcoder/Downloads/gmbadb/csvs/v2-LU_RedListCategories.csv --model_name RedListCategory --app_name app def handle(self, *args, **options): csv.register_dialect( 'mydialect', delimiter=',', quotechar='"', doublequote=True, skipinitialspace=True, lineterminator='\n', quoting=csv.QUOTE_MINIMAL) csv_folder_path = '/home/pcoder/Downloads/gmbadb/csvs' if options['csv_folder_path']: csv_folder_path = options['csv_folder_path'] debug = options.get('debug', None) if options.get('all'): print("Doing an import of all csvs") for csv_file_name, model_name in self.csv_files_models_dict.items(): print("Importing %s -- %s" % (csv_file_name, model_name)) models_to_ignore = ['Range', 'NamesImport', 'ImportGeom210915', 'Organization', 'AddElevation', 'GMBA_V2_Centroid', 'Person', 'PeopleRange', 'PeopleFunction', "PeopleResource", "RangeCountry", "RangeNameTranslation", "RangeOnlineInfo", "ResourceRange", "ResourceKeyword", "Repository"] models_to_ignore = [] if model_name in models_to_ignore: # we have already imported and do not want to spend more time redoing stuff continue if csv_folder_path.endswith('/'): file_path = '%s%s' % (csv_folder_path, csv_file_name) else: file_path = '%s/%s' % (csv_folder_path, csv_file_name) _model = apps.get_model(options.get('app_name', 'app'), model_name) with open(file_path, 'r') as csv_file: reader = csv.reader(csv_file, dialect='mydialect') first = True for row in reader: if first: # Assume the first row to be the header header = row header = [h.strip('"') for h in header] first = False continue _object_dict = {str(self.cols_to_django_fields.get(key)): str(value.lstrip('"').rstrip('"')) for key, value in zip(header, row)} _object_dict = handle_object_dict(_object_dict, model_name) m = _model(**_object_dict) try: m.save() except IntegrityError as ie: print(str(ie)) if "UNIQUE constraint failed: range.gmba_v2_id" in str(ie): print("======") print("Could not save %s" % json.dumps(_object_dict)) print("======") print("Done importing %s" % model_name) else: _model = apps.get_model(options.get('app_name', 'app'), options['model_name']) model_name = options['model_name'] mother_range_reload = options.get('mother_range_reload') k = '' csv_file_name = '' for k, v in self.csv_files_models_dict.items(): if v.strip().lower() == model_name.strip().lower(): csv_file_name = k if csv_file_name == '': raise Exception('Could not find a csv file name for model %s' % model_name) if csv_folder_path.endswith('/'): file_path = '%s%s' % (csv_folder_path, csv_file_name) else: file_path = '%s/%s' % (csv_folder_path, csv_file_name) csv.register_dialect( 'mydialect', delimiter=',', quotechar='"', doublequote=True, skipinitialspace=True, lineterminator='\n', quoting=csv.QUOTE_MINIMAL) with open(file_path, 'r', newline='') as csv_file: reader = csv.reader(csv_file, dialect='mydialect') first = True error_row_count = 0 total = 0 for row in reader: total += 1 if first: # Assume the first row to be the header header = row header = [h.strip('"') for h in header] first = False continue if mother_range_reload: # We have loaded the range model already and we are attempting to construct the mother_range # relationship try: this_range = MountainRange.objects.get(id=int(row[0])) r = MountainRange.objects.get(id=int(row[5])) # default Range which exists already this_range.mother_range = r this_range.save() except MountainRange.DoesNotExist as dne: print("this range = %s, mother range --> %s" % (row[0], row[5])) print(str(dne)) error_row_count += 1 except ValueError as ve: print('0 = %s and 5 = %s' % (row[0], row[5])) print(str(ve)) error_row_count += 1 continue _object_dict = {self.cols_to_django_fields.get(key): value.lstrip('"').rstrip('"') for key, value in zip(header, row)} _object_dict = handle_object_dict(_object_dict, model_name, debug=debug) try: m = _model(**_object_dict) m.save() except django.db.utils.IntegrityError as ex1: error_row_count += 1 print(str(ex1)) print("**********************") print(str(_object_dict)) print("**********************") except Exception as ex: error_row_count += 1 print('-----') print(str(ex)) print('----------------') continue print("Done importing %s" % str(_model)) print("Total rows = %s, error rows = %s" % (total, error_row_count)) def handle_object_dict(object_dict, model_name, debug=False): if model_name == 'Resource': object_dict['url'] = object_dict['url'].strip("#") for i in ['PEGASuS_Check_map_with_author', 'gloria', 'gnomo', 'lter', 'ltser', 'miren', 'team', 'inventory']: if i in object_dict: object_dict[i] = True if object_dict[i].lower().strip() == 'true' else False if model_name == 'MountainRange': # Reinstate range_name key object_dict['range_name'] = object_dict['range_name_id'] object_dict.pop('range_name_id') connected_fields = [{'range_name_language': Language}, {'feature': RangeType}] for f in connected_fields: for k, v in f.items(): if k in object_dict: if debug: print('Getting %s of %s' % (k, object_dict[k])) if object_dict[k] == '': object_dict[k] = 0 object_dict[k] = v.objects.get(id=int(object_dict[k])) if 'mother_range' in object_dict: if debug: print('Getting mother_range of %s' % object_dict['mother_range']) if object_dict['mother_range'] == '': object_dict['mother_range'] = 0 try: object_dict['mother_range'] = MountainRange.objects.get(id=0) # default Range which exists already except MountainRange.DoesNotExist as dne: print(str(dne)) for i in ['checked']: if i in object_dict: object_dict[i] = True if object_dict[i].lower().strip() == 'true' else False for i in ['select_300']: if i in object_dict: object_dict[i] = True if object_dict[i].lower().strip() == 'x' else False for i in ['gmba_narrow']: if i in object_dict: object_dict[i] = True if object_dict[i].lower().strip() == 'x' else False # area field can't be empty if 'area' in object_dict: if object_dict['area'] == '': object_dict['area'] = -1 if model_name == 'Keyword': if object_dict['mother'] == '': object_dict['mother'] = 0 try: object_dict['mother'] = Keyword.objects.get(keyword_id=object_dict['mother']) # default Keyword which exists already except Keyword.DoesNotExist as dne: object_dict['mother'] = Keyword.objects.get(keyword_id=0) print(str(dne)) if model_name == 'Organization' and 'country_id' in object_dict: object_dict['country'] = object_dict['country_id'] object_dict.pop('country_id') if model_name == 'PeopleRange' and 'mountain_range' in object_dict: object_dict['range_id'] = object_dict['mountain_range'] object_dict.pop('mountain_range') if model_name == 'Species' and 'scientific_name_id' in object_dict: object_dict['scientific_name'] = object_dict['scientific_name_id'] object_dict.pop('scientific_name_id') if model_name == 'TaxonRange' and 'taxon_id' in object_dict: object_dict['taxon'] = object_dict['taxon_id'] object_dict.pop('taxon_id') if model_name == 'Person' and 'organization_id' in object_dict: if debug: print("organization_id=%s" % object_dict['organization_id']) if object_dict['organization_id'] == '' or object_dict['organization_id'] is None: object_dict['organization_id'] = '-1' else: object_dict['organization_id'] = int(float(object_dict['organization_id'])) if 'status' in object_dict: if debug: print('Getting status of %s' % object_dict['status']) if object_dict['status'] == '': object_dict['status'] = 0 object_dict['status'] = PeopleStatus.objects.get(id=int(object_dict['status'])) if 'country_lookup' in object_dict: if object_dict['country_lookup'].strip() == '' or object_dict['country_lookup'] is None: object_dict['country_lookup'] = 0 if debug: print('Getting country of %s' % object_dict['country_lookup']) object_dict['country'] = Country.objects.get(id=int(object_dict['country_lookup'])) object_dict.pop('country_lookup') for i in ['news_letter', 'birds', 'mammals', 'reptiles', 'amphibians', 'fish', 'insects', 'molluscs', 'crustaceans', 'arachnids', 'angiosperms', 'gymnosperms', 'fungi', 'algae', 'microbes', 'biological_field_sampling', 'data_mining', 'remote_sensing', 'gis', 'spatial_analysis', 'statistical_analysis', 'modelling', 'assessment', 'meta_analysis', 'synthesis', 'qualitative_ssm', 'genetic_analyses', 'field_site', 'transect', 'mountain_top', 'mountain_range', 'landscape', 'regional', 'national', '_global', 'profile_on_web', 'updated']: if i in object_dict: object_dict[i] = True if object_dict[i].lower().strip() == 'true' else False if object_dict is None: if debug: print("Object None for %s" % model_name) # else: # print(object_dict) if str(object_dict) is not None else "Str object dict is None" return object_dict