Adding new keys/values to nested dictionary in geojson file - python

I have a .js containing data used to plot points on a map. In the file, I have a single line var statement which contains nested dictionaries.
var json_Project = {"type":"FeatureCollection","name":"project","crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}},"features":[{"type":"Feature","properties":{"ID":"001","field1":"someText","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}},{"type":"Feature","properties":{"ID":"002","field1":"someText","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}}]}
One of these contains the "features" key which stores a list of dictionaries for each point (see below for readability).
var json_Project = {"type":"FeatureCollection","name":"project","crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}},"features":[
{"type":"Feature","properties":{"ID":"001","field1":"someText","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}},
{"type":"Feature","properties":{"ID":"002","field1":"someText","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}}]}
The "properties" for the first point has "ID":"001", second point has "ID":"001" etc.
I would like to insert a new property for each point with "INFORMATION:" and get the value from another dictionary using the keys from "ID".
info_dict = {'001': 'This is Project 1',
'002': 'This is Project 2'}
Specifically, I would like this new property to be inserted before "field2" so that it looks like:
var json_Project = {"type":"FeatureCollection","name":"project","crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}},"features":[
{"type":"Feature","properties":{"ID":"001","field1":"someText","INFORMATION":"This is Project 1","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}},
{"type":"Feature","properties":{"ID":"002","field1":"someText","INFORMATION":"This is Project 2","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}}]}
My futile attempt involved converting the one-liner to a string and then extract everything after the "features:" string as that is unique in the file. But not sure how to add the new key/value pair.
with open('project.js') as f:
contents = f.read().split()
contents_toString = ''.join(contents)
new_contents = re.findall('(?<="features":\[).*$', contents_toString)
EDIT
Thanks to #TenaciousB, I can read the geojson file and add in the "INFORMATION" property with the correct values from the dictionary:
with open('project.js') as f:
contents = f.read()
x = json.loads(contents)
for y in x['features']:
key = y['properties']['ID']
description = dictionary[key]
y['properties']['INFORMATION'] = description
I am still unsure how to:
Remove var json_Project = programmatically before being read;
Place the "INFORMATION" property before "field2";
Re-insert var json_Project = , save and ensure the single-line format is retained.

Related

Python docx module to modify multiple files

I am trying to use small, nested for-loops to iterate through a list of values, replace items in a Word doc with the docx module, changing one value each loop, and saving a new doc each time and naming it as the changed value's name. Everything works except the value I'm trying to change with each loop is not updating on the Word files. The first value in my list is the value that shows up on every Word doc. When I output/save the files, the filename is correctly updating - but the content of the value on the document is always the same. Here's the code:
def makeWord(ident): #'ident' is a list of alphanumeric I.D. numbers
wordpath = path + 'Word_Template.docx'
outpath = path + 'Word Files\\'
wordfile = Document(wordpath)
for cn in ident:
myvars = {'$MO':datamonth, '$YEAR':datayear, '$idnum', cn} #data month/year are constants
for key, val in myvars.items():
for para in wordfile.paragraphs:
replace_text_in_paragraph(para, key, val)
saveword(wordfile, cn, outpath)
def replace_text_in_paragraph(paragraph, key, value):
if key in paragraph.text:
inline = paragraph.runs
for item in inline:
if key in item.text:
item.text = item.text.replace(key, value)
def saveword(wordfile, idnum, outp):
wordfile.save(outp + idnum + '_' + datamonth + '_' + datayear + '.docx')
My list of values is in the "ident" list. If the first value in the list is "B25", that value is placed in all of my Word docs, though the filename of the Word docs appropriately changes as the loop runs. I have been stuck for two days on this. Any help is greatly appreciated!
If your goal is to replace words in docx files with variables then my advice would be to use the docxtpl library.
You can open the file you want to edit, then use a dictionary to replace values, it's much cleaner-
Heres an example that gives you an idea of how powerful this is:
# If you have multiple forms to do in a list
for form in forms:
# Document
doc = DocxTemplate(form[0])
# Feed variables in as a Docx Jinga2 will convert on-page
context = {
'text_in_doc' : "something",
'text_in_doc2' : "something",
'text_in_doc3': "something",
}
# Render new Doc with Jinga2 swapped
doc.render(context)
# Save new Doc after Jinga2 conversion
doc.save("/save/to/directory/sample.docx")
GL

Can I import dictionary items with the same values into a list?

I'm importing data from a text file, and then made a dictionary out of that. I'm now trying to make a separate one, with the entries that have the same value only. Is that possible?
Sorry if that's a little confusing! But basically, the text file looks like this:
"Andrew", "Present"
"Christine", "Absent"
"Liz", "Present"
"James", "Present"
I made it into a dictionary first, so I could group them into keys and values, and now I'm trying to make a list of the people who were 'present' only (I don't want to delete the absent ones, I just want a separate list), and then pick one from that list randomly.
This is what I tried:
d = {}
with open('directory.txt') as f:
for line in f:
name, attendance = line.strip().split(',')
d[name.strip()] = attendance.strip()
present_list = []
present_list.append({"name": str(d.keys), "attendance": "Present"})
print(random.choice(present_list))
When I tried running it, I only get:
{'name': '<built-in method keys of dict object at 0x02B26690>', 'attendance': 'Present'}
Which part should I change? Thank you so much in advance!
You can try this:
present_list = [key for key in d if d[key] == "Present"]
first, you have to change the way you the read lines than you can have in your initial dict as key the attendence :
from collections import defaultdict
d = defaultdict(list)
with open('directory.txt') as f:
for line in f.readlines():
name, attendance = line.strip().split(',')
d[attendance.strip()].append(name.strip())
present_list = d["Present"]
print(random.choice(present_list) if present_list else "All absent")
Dict.Keys is a method, not a field. So you must instead do:
d.keys()
This returns an array generator: if you want a comma separated list with square brackets, just calling str() on it is ok. If you want a different formatting, consider ','.join(dict.keys()) to do a simple comma separated list with no square brackets.
UPDATE:
You also have no filtering in place, instead I'd try something like this, where you grab the list of statuses and then compile (new code in BOLD):
d = {}
with open('directory.txt') as f:
for line in f:
name, attendance = line.strip().split(',')
**if name.strip() not in d.keys():
d[attendance.strip()] = [name.strip()]
else:
d[attendance.strip()] = d[attendance.strip()].append(name.strip())**
This way you don't need to go through all those intermediate steps, and you will have something like {"present": "Andrew, Liz, James"}

For List: NoneType object has no attribute append

When I try to append strings to an list-value in my dictionary, I keep getting this error, so when I tried to check the type of my object (see second line in code from the bottom) it came back with "unhashable type: list".
I am attempting to take info from a CSV and put it in a dictionary. The CSV has a list of name with comments associated with those names. Many of the names reoccur with multiple comments so I am trying to store all of these comments in a list which will be the value while the authors name will be the key.
Ex:
dictionary = {'Mike': 'Hey there', 'Grandma': 'Good morning', 'Mike': 'Yes it is'}
would actually be:
dictionary = {'Mike': ['Hey there', 'Yes it is'], 'Grandma': ['Good morning']}
as this way, when I am receiving the data, I can simply add in another comment if need. Such as if I get a new comment for Mike that's 'Goodbye' I can simply append this to his list of values.
My code is as follows:
def extract_data():
data_dict = {}
with open(data_CSV_file,'rt') as file:
#to avoid complications with the null byte
data = csv.reader(x.replace('\0', '') for x in file)
count = 0
# keep list of authors to check when we're adding
list_of_authors = []
# get data from CSV
for row in data:
if count < 400:
# check if we've already added this person
if row[AUTHOR] not in list_of_authors:
list_of_comments = [row[COMMENT]]
data_dict.update({row[AUTHOR]: list_of_comments})
list_of_authors.append(row[AUTHOR])
count += 1
# if so, simply add to their list of comments
else:
cvalue = data_dict.get(row[AUTHOR])
#print(type(data_dict.get(cvalue)))
data_dict[row[0]] = cvalue.append(row[1])
return data_dict
Since I'm reading in the data from a CSV file, I figured this would be the best way as the CSV file isn't organized so I don't know where the authors next comment will come up and don't want to have to search through the entire CSV file to get all of their comments every time I come across a new name.

Python removing a comma from last line within a for loop

Id like to know what it takes to remove a comma from the last line within a for loop in python. When I run the script it gives me the below output(after code section). I want to remove the comma at the end of fourth line "{"{#MACRO}":"queue4"}," Please can someone help?
By the way if there is a better way to construct the block please share the ideas. I'm a beginner and like to learn. :)
Code:
import json
import urllib
import string
Url= "http://guest:guest#localhost:55672/api/queues"
Response = urllib.urlopen(Url)
Data = Response.read()
def Qlist(Name):
Text = ''' {{"{{#MACRO}}":"{Name}"}},'''.format(Name=Name)
print Text
X_json = json.loads(Data)
print '''{
"data":['''
for i in X_json:
VV = i['name']
Qlist(VV)
print ''']
}'''
Below is the Output:
{
"data":[
{"{#MACRO}":"queue1"},
{"{#MACRO}":"queue2"},
{"{#MACRO}":"queue3"},
{"{#MACRO}":"queue4"},
]
}
Thanks so much
You can modify your loop as follows.
# Create and initialize a dictionary (Associative Array)
# data['data'] is an empty list.
# Variable name (data in this case) can be anything you want.
# 'data' is a key. notice the quotations around. it's not a variable.
# I used 'data' as the key, becasue you wanted your final output to include that part.
data = {"data": []}
for i in X_json:
# We are not calling the data dictionary here.
# We are accessing the empty list we have created inside the `data` dict (above) using data['data'] syntax.
# We can use the append function to add an item to a list.
# We create a new dictionary for every `name` item found in your json array and
# append that new dictionary to the data['data'] list.
data['data'].append({"{#MACRO}": i['name']})
print(json.dumps(data))
# or print json.dumps(data, indent=True)
Read more about json.dumps() here. You can read more about python's list and dictionary here
Don't print inside Qlist - instead return a value; then you can join all returned values using comma as separator:
def Qlist(Name):
Text = ''' {{"{{#MACRO}}":"{Name}"}}'''.format(Name=Name)
return Text
print '''{
"data":[''' +
',\n'.join([ Qlist(i['name']) for i in X_json ]) +
''']
}'''
And anyway, using json.dumps is likely a better idea.

Python - convert text file to dict and convert to json

How can I convert this text file to json? Ultimately, I'll be inserting the json blobs into a NoSQL database, but for now I plan to parse the text files and build a python dict, then dump to json.
I think there has to be a way to do this with a dict comprehension that I'm just not seeing/following (I'm new to python).
Example of a file:
file_1.txt
[namespace1] => metric_A = value1
[namespace1] => metric_B = value2
[namespace2] => metric_A = value3
[namespace2] => metric_B = value4
[namespace2] => metric_B = value5
Example of dict I want to build to convert to json:
{ "file1" : {
"namespace1" : {
"metric_A" : "value_1",
"metric_B" : "value_2"
},
"namespace2" : {
"metric_A" : "value_3",
"metric_B" : ["value4", "value5"]
}
}
I currently have this working, but my code is a total mess (and much more complex than this example w/ clean up etc). I'm basically going line by line through the file, building a python dict. I check each namespace for existence in the dict, if it exists, i check the metric. If the metric exists already, I know I have duplicates and need to convert the value to an array that contains the existing value and my new value(s). There has to be a more simple/clean way.
import glob
import json
answer = {}
for fname in glob.glob(file_*.txt): # loop over all filenames
answer[fname] = {}
with open(fname) as infile:
for line in infile:
line = line.strip()
if not line: continue
splits = line.split()[::2]
splits[0] = splits[0][1:-1]
namespace, metric, value = splits # all the values in the line that we're interested in
answer[fname].get(namespace, {})[metric] = value # populate the dict
required_json = json.dumps(answer) # turn the dict into proper JSON
You can use regex for that. re.findall('\w+', line) will find all text groups which you are after, then the rest is saving it in the dictionary of dictionary. The simplest way to do that is to use defaultdict from collections.
import re
from collections import defaultdict
answer = defaultdict(lambda: defaultdict(lambda: []))
with open('file_1.txt', 'r') as f:
for line in f:
namespace, metric, value = re.findall(r'\w+', line)
answer[namespace][metric].append(value)
As we know, that we expect exactly 3 alphanum groups, we assign it to 3 variable, i.e. namespace, metric, value. Finally, defaultdict will return defaultdict for the case when we see namespace first time, and the inner defaultdict will return an empty array for first append, making code more compact.

Categories