How to split a text file into a nested array? - python

Working on a project creating a python flask website that stores user logins into a text file. I have a text file where each line is one user and each user has 5 parameters stored on the line. All user parameters are separated by a ; character.
Parameters are:
username
password
first name
last name
background color
title
avatar
Sample of the text file:
joebob;pass1;joe;bob;yellow;My title!!;https://upload.wikimedia.org/wikipedia/commons/c/cd/Stick_Figure.jpg
richlong;pass2;rich;long;blue;My title2!!;https://www.iconspng.com/images/stick-figure-walking/stick-figure-walking.jpg
How do I go about storing the parameters into a python array, and how do I access them later when I need to reference log-ins.
Here is what I wrote so far:
accounts = { }
def readAccounts():
file = open("assignment11-account-info.txt", "r")
for accounts in file: #line
tmp = accounts.split(';')
for data in tmp: #data in line
accounts[data[0]] = {
'user': data[0],
'pass': data[1],
'first': data[2],
'last': data[3],
'color': data[4],
'title': data[5],
'avatar': data[6].rstrip()
}
file.close()

You can use the python builtin csv to parse
import csv
with open("assignment11-account-info.txt", "r") as file:
reader = csv.reader(file, delimiter=';')
result = []
for row in reader:
fields = ('user', 'passwd', 'first', 'last', 'color','title','avatar')
res = dict(zip(fields, row))
result.append(res)
Or equivalent but harder to read for a beginner the pythonic list comprehension:
with open("assignment11-account-info.txt", "r") as file:
reader = csv.reader(file, delimiter=';')
fields = ('user', 'passwd', 'first', 'last', 'color','title','avatar')
result = [ dict(zip(fields, row)) for row in reader ]

Here's what I might do:
accounts = {}
with open("assignment11-account-info.txt", "r") as file:
for line in file:
fields = line.rstrip().split(";")
user = fields[0]
pass = fields[1]
first = fields[2]
last = fields[3]
color = fields[4]
title = fields[5]
avatar = fields[6]
accounts[user] = {
"user" : user,
"pass" : pass,
"first" : first,
"last" : last,
"color" : color,
"title" : title,
"avatar" : avatar
}
By using with, the file handle file is closed for you automatically. This is the most "Python"-ic way of doing things.
So long as user is unique, you won't overwrite any entries you put in as you read through the file assignment11-account-info.txt.
If you need to deal with a case where user is repeated in the file assignment11-account-info.txt, then you need to use an array or list ([...]) as opposed to a dictionary ({...}). This is because reusing the value of user will overwrite any previous user entry you add to accounts. Overwriting existing entries is almost always a bad thing when using dictionaries!
If that is the case, I might do the following:
accounts = {}
with open("assignment11-account-info.txt", "r") as file:
for line in file:
fields = line.rstrip().split(";")
user = fields[0]
pass = fields[1]
first = fields[2]
last = fields[3]
color = fields[4]
title = fields[5]
avatar = fields[6]
if user not in accounts:
accounts[user] = []
accounts[user].append({
"user" : user,
"pass" : pass,
"first" : first,
"last" : last,
"color" : color,
"title" : title,
"avatar" : avatar
})
In this way, you preserve any cases where user is duplicated.

Related

How to read txt file data and convert into nested dictionary?

I have this txt file but I'm having trouble in converting it into a nested dictionary in python. The txt file only has the values of the pokemon but are missing the keys such as 'quantity' or 'fee'. Below is the content in the txt file. (I have the ability to change the txt file if needed)
charmander,3,100,fire
squirtle,2,50,water
bulbasaur,5,25,grass
gyrados,1,1000,water flying
This is my desired dictionary:
pokemon = {
'charmander':{'quantity':3,'fee':100,'powers':['fire']},
'squirtle':{'quantity':2,'fee':50,'powers':['water']},
'bulbasaur':{'quantity':5,'fee':25,'powers':['grass']},
'gyrados':{'quantity':1,'fee':1000,'powers':['water','flying']}
}
Convert text file to lines, then process each line using "," delimiters. For powers, split the string again using " " delimiter. Then just package each extracted piece of information into your dict structure as below.
with open('pokemonInfo.txt') as f:
data = f.readlines()
dict = {}
for r in data:
fields = r.split(",")
pName = fields[0]
qty = fields[1]
fee = fields[2]
powers = fields[3]
dict[pName] = {"quantity": qty, "fee": fee, "powers": [p.strip() for p in powers.split(" ")]}
for record in dict.items():
print(record)

How to get information with python when data is heavily nested

I have a text file which contains some data to be mined.
The structure is shown below
name (personA {
field1 : data1
field2 : data2
fieldN : dataN
subfield() {
fieldx1 : datax1
fieldxN : dataxN
}
}
name (personB {
field1 : data11
field2 : data12
fieldN : data1N
}
In some person's record the subfield is absent and output should specify subfield to be unknown in that case. Now below is the code I use to extract the data
import re
data = dict()
with open('data.txt', 'r') as fin:
FLAG, FLAGP, FLAGS = False, False, False
for line in fin:
if FLAG:
if re.search('field1', line):
d1 = line.split()[2]
data['field1'] = d1
if re.search('fieldN', line):
dN = line.split()[2]
data['fieldN'] = dN
data['fieldxn'] = 'unknown'
FLAGP = True
if FLAGS:
if re.search('fieldxN', line):
dsN = line.split()[2]
data['fieldxn'] = dsN
if re.search('name\ \(', line):
pn = line.split()[1]
FLAG = True
data['name'] = pn
if re.search('subfield', line):
FLAGS = True
if len(data) == 4:
if FLAGP:
print data
FLAGP = False
FLAG = False
FLAGS = False
The output is shown below
{'field1': 'data1', 'fieldN': 'dataN', 'name': '(personA', 'fieldxn': 'unknown'}
{'field1': 'data11', 'fieldN': 'data1N', 'name': '(personB', 'fieldxn': 'unknown'}
The problem has been that I don't know where to print data so current I am using below statment to print data which is wrong
if len(data) == 4:
if FLAGP:
print data
FLAGP = False
FLAG = False
FLAGS = False
I would appreciate if someone could give any suggestion to retrieve the data correctly
I would take a different approach to parsing, storing the subfields (and other fields) in a dictionary.
data = open('data.txt', 'rt').read()
### Given a string containing lines of "fieldX : valueY"
### return a dictionary of values
def getFields(field_data):
fields = {}
if (field_data != None):
field_lines = field_data.strip().split("\n")
for pair in field_lines:
name, value = pair.split(":")
fields[name.strip()] = value.strip()
return fields
### Split the data by name
people_data = data.strip().split("name (")[1:]
### Loop though every person record
for person_data in people_data:
name, person_data = person_data.split(" {", 1) # split the name and the fields
# Split out the subfield data, if any
subfield_data = None
if (person_data.find("subfield()") > -1):
field_data, subfield_data = person_data.split("subfield() {", 1)
subfield_data = subfield_data.split("}")[0]
# Separate the fields into single lines of pairs
fields = getFields(field_data)
# and any subfields
subfields = getFields(subfield_data)
print("Person: "+str(name))
print("Fields: "+str(fields))
print("Sub_Fields:"+str(subfields))
Which gives me:
Person: personA
Fields: {'field1': 'data1', 'field2': 'data2', 'fieldN': 'dataN'}
Sub_Fields:{'fieldx1': 'datax1', 'fieldxN': 'dataxN'}
Person: personB
Fields: {'field1': 'data1', 'field2': 'data2', 'fieldN': 'dataN'}
Sub_Fields:{}
So you could just adjust your output based on whether subfields was None, or otherwise. The idea is to get your data input into more flexible structures, rather than "brute-force" parsing like you have done. In the above I use split() a lot to give a more flexible way through, rather than relying on finding exact names. Obviously it depends on your design requirements too.

How to unlink One2many Field values on onchange in Odoo

I created a module to add sale order lines as per customer to PO Order lines.
Selecting customer
When i add Sale Order it will be added in PO order.
SO added
I was trying to unlink a specific ids from one2many field. in picture Add SO fields
class PurchaseOrder(models.Model):
_inherit= "purchase.order"
_name = "purchase.order"
order_ids = fields.Many2many('sale.order', String="Add Order",domain="[('partner_id', 'child_of', partner_id),('state', 'in', ('quotation','socreated','done'))]")
#api.onchange('order_ids')
def orders_change(self):
if not self.order_ids:
return {}
if not self.partner_customer_id:
warning = {
'title': _('Warning!'),
'message': _('You must first select a partner!'),
}
# self.order_ids =False
return {'warning': warning}
line_ids = []
u_ids=[]
new_lines = self.env['purchase.order.line']
for qt in self.order_ids:
for i in self.order_line.mapped('sale_order_id'):
line_ids.append(i)
for u in self.order_ids:
if u.id in line_id:
u_ids.append(u)
line_ids.remove(u)
if line_ids and u_ids:
lp = self.order_line.filtered(lambda r: r.sale_order_id <= line_ids[0])
lp2 = self.order_line.filtered(lambda r: r.sale_order_id <= u_ids[0])
for line in self.order_line:
if line in lp:
# self.order_line = [(6, 0, lp2.ids)]
line.unlink()
continue
for line in qt.order_line:
# Load a PO line only once
if line in self.order_line.mapped('sale_order_line_id'):
continue
#seller section
seller = line.product_id._select_seller(
line.product_id,
partner_id=self.partner_id,
quantity=line.product_uom_qty,
date=self.date_order and self.date_order[:10],
uom_id=line.product_uom)
price_unit = self.env['account.tax']._fix_tax_included_price(seller.price,
line.product_id.supplier_taxes_id,
line.tax_id) if seller else 0.0
if price_unit and seller and self.currency_id and seller.currency_id != self.currency_id:
price_unit = seller.currency_id.compute(price_unit, self.currency_id)
if seller and line.product_uom and seller.product_uom != line.product_uom:
price_unit = self.env['product.uom']._compute_price(seller.product_uom.id, price_unit,
to_uom_id=line.product_uom.id)
unit = price_unit
qty = line.product_uom_qty
if float_compare(qty, 0.0, precision_rounding=line.product_uom.rounding) <= 0:
qty = 0.0
tax_id = line.tax_id or line.product_id.taxes_id
data = {
'sale_order_line_id': line.id,
'name': line.name,
'sequence_number':line.sequence_number,
'product_id': line.product_id.id,
'product_qty': qty,
'product_uom': line.product_id.uom_po_id or line.product_id.uom_id,
'price_unit': unit,
'cpo_no' : line.order_id.cpo_number,
'cpo_product_qty': qty,
'cpo_product_uom': line.product_id.uom_id,
'cpo_price_unit': line.price_unit,
'discount': 0.0,
'date_planned':(datetime.today() + relativedelta(weeks=4)).strftime(DEFAULT_SERVER_DATETIME_FORMAT),
}
new_line = new_lines.new(data)
new_line._set_additional_fields(self)
new_lines += new_line
if new_lines :
self.order_line += new_lines
class PurchaseOrderLine(models.Model):
_inherit= "purchase.order.line"
_name = "purchase.order.line"
sale_order_line_id = fields.Many2one('sale.order.line', 'Order Line', ondelete='set null', select=True
)
sale_order_id = fields.Many2one('sale.order', related='sale_order_line_id.order_id', string='Order',
store=False)
When i remove a order_ids , i want to unlink related lines from order_line (po)
link_ids will hold order_ids when it selected, when a id removed from order_ids it will be removed from link_ids.
u_ids will hold the rest of order_id when it deleted.
when i remove a id from order_ids iwant to unlink the related line from order_line
but i cant delete it.
i have user [6,0,ids] method to replace values , it wont work in create state.
Please help me.
new_lines = self.env['purchase.order.line']
for qt in self.order_ids:
for i in self.order_line.mapped('sale_order_id'):
line_id.append(i.id)
line_ids.append(i)
for u in self.order_ids:
if u.id in line_id:
u_id.append(u.id)
line_id.remove(u.id)
u_ids.append(u)
line_ids.remove(u)
k = self.order_line.mapped('sale_order_id')
if line_id and line_ids and u_ids:
lp = self.order_line.filtered(lambda r: r.sale_order_id <= line_ids[0])
lp6 = self.order_line.filtered(lambda r: r.sale_order_id not in line_ids)
lp2 = self.order_line.filtered(lambda r: r.sale_order_id in u_ids)
for line in self.order_line:
if line in lp:
self.update({
'order_line': [(5, _, _)],
})
for o in lp6:
data = {
'sale_order_line_id': o.sale_order_line_id.id,
'name': o.name,
'sequence_number': o.sequence_number,
'product_id': o.product_id.id,
'product_qty': o.product_qty,
'product_uom': o.product_uom,
'price_unit': o.price_unit,
'cpo_no': o.cpo_no,
'cpo_product_qty': o.cpo_product_qty,
'cpo_product_uom': o.cpo_product_uom,
'cpo_price_unit': o.cpo_price_unit,
# 'quote_ref':line.order_id.origin,
'discount': 0.0,
'date_planned': (datetime.today() + relativedelta(weeks=4)).strftime(
DEFAULT_SERVER_DATETIME_FORMAT),
}
for line in qt.order_line:
# Load a PO line only once
if line in self.order_line.mapped('sale_order_line_id'):
continue
new_line = new_lines.new(data)
new_line._set_additional_fields(self)
new_lines += new_line
new_line = False
continue
Sorry i'm late it's been a log time:
try to remove every record by using [(5,0,0)] then add the ids. this worked for me but the only problem is that it work if an other field trigger the onchange method not the same many2many or one2many field
but in create mode if you didn't find any solution why don't you use update button when he change the selected order ids he needs to validate before showing the one2many field and this way you will work on write always because create will happen when he validate the the choices. what i'm trying to say is find what it work and work arround it to find a way to use it
For the field you used to make relation:-
sale_order_id = fields.Many2one('sale.order', related='sale_order_line_id.order_id', string='Order',
store=False)
please try to give attribute ondelete = 'cascade' and change the line as :-
sale_order_id = fields.Many2one('sale.order', related='sale_order_line_id.order_id', string='Order', ondelete ='cascade'
store=False)
If you put like this , when the related sale order is deleted the one2many entry with relation record will be also removed. This is just an example and you could try this way.
Thanks
Following is a common logic for deleting a record in one2many
#api.onchage('field_name')
def onchange_field_name(self):
for line in self.one2many_field_records:
if line.order_id == 'your_satisfying_condition':
line.unlink()
This is the usual way to delete the order line records

Python: Reorder columns of a csv file

So I am collecting data and this data is saved into csv files, however for presentation purposes I want to reorder the columns in each respective csv file based on it's related "order".
I was using this question (write CSV columns out in a different order in Python) as a guide but I'm not sure why I'm getting the error
writeindices = [name2index[name] for name in writenames]
KeyError: % Processor Time
when I run it. Note this error doesn't seem to be limited to just the string % Processor Time'.
Where am I going wrong?
Here is my code:
CPU_order=["%"+" Processor Time", "%"+" User Time", "Other"]
Memory_order=["Available Bytes", "Pages/sec", "Pages Output/sec", "Pages Input/sec", "Page Faults/sec"]
def reorder_csv(path,title,input_file):
if title == 'CPU':
order=CPU_order
elif title == 'Memory':
order=Memory_order
output_file=path+'/'+title+'_reorder'+'.csv'
writenames = order
reader = csv.reader(input_file)
writer = csv.writer(open(output_file, 'wb'))
readnames = reader.next()
name2index = dict((name, index) for index, name in enumerate(readnames))
writeindices = [name2index[name] for name in writenames]
reorderfunc = operator.itemgetter(*writeindices)
writer.writerow(writenames)
for row in reader:
writer.writerow(reorderfunc(row))
Here is a sample of what the input CSV file looks like:
,CPU\% User Time,CPU\% Processor Time,CPU\Other
05/23/2016 06:01:51.552,0,0,0
05/23/2016 06:02:01.567,0.038940741537158409,0.62259056657940626,0.077882481554869071
05/23/2016 06:02:11.566,0.03900149141703179,0.77956981074955856,0
05/23/2016 06:02:21.566,0,0,0
05/23/2016 06:02:31.566,0,1.1695867249963632,0
Your code works. It is your data which does not have a column named "% Processor Time". Here is a sample data I use:
Other,% User Time,% Processor Time
o1,u1,p1
o2,u2,p2
And here is the code which I call:
reorder_csv('.', 'CPU', open('data.csv'))
With these settings, everything works fine. Please check your data.
Update
Now that I see your data, it looks like your have column names such as "CPU\% Processor Time" and want to translate it to "% Processor Time" before writing out. All you need to do is creating your name2index this way:
name2index = dict((name.replace('CPU\\', ''), index) for index, name in enumerate(readnames))
The difference here is instead of name, you should have name.replace('CPU\\', ''), which get rid of the CPU\ part.
Update 2
I reworked your code to use csv.DictReader and csv.DictWriter. I also assume that "CPU\% Prvileged Time" will be transformed into "Other". If that is not the case, you can fix it in the transformer dictionary.
import csv
import os
def rename_columns(row):
""" Take a row (dictionary) of data and return a new row with columns renamed """
transformer = {
'CPU\\% User Time': '% User Time',
'CPU\\% Processor Time': '% Processor Time',
'CPU\\% Privileged Time': 'Other',
}
new_row = {transformer.get(k, k): v for k, v in row.items()}
return new_row
def reorder_csv(path, title, input_file):
header = dict(
CPU=["% Processor Time", "% User Time", "Other"],
Memory=["Available Bytes", "Pages/sec", "Pages Output/sec", "Pages Input/sec", "Page Faults/sec"],
)
reader = csv.DictReader(input_file)
output_filename = os.path.join(path, '{}_reorder2.csv'.format(title))
with open(output_filename, 'wb') as outfile:
# Create a new writer where each row is a dictionary.
# If the row contains extra keys, ignore them
writer = csv.DictWriter(outfile, header[title], extrasaction='ignore')
writer.writeheader()
for row in reader:
# Each row is a dictionary, not list
print row
row = rename_columns(row)
print row
print
writer.writerow(row)

Python script to create multiple users in a CSV file and generate email addresses

I want to create a csvfile that has multiple users and at the same time create email addresses for this users using their last names. I am using python for this but I can't get it to create the e-mail address in the list. My script is below, what am I missing?
import csv
First_Name = ["Test"]
Last_Name = ["User%d" % i for i in range (1,10)]
Email_Address = 'Last_Name' [("#myemail.com")]
Password = ["Password1"]
# open a file for writing.
csv_out = open('mycsv.csv', 'wb')
# create the csv writer object.
mywriter = csv.writer(csv_out)
# all rows at once.
rows =zip(Email_Address, Password, First_Name, Last_Name,)
mywriter.writerows(rows)
csv_out.close()
Make
Email_Address = 'Last_Name' [("#myemail.com")]
into
Email_Address = [x + "#myemail.com" for x in Last_Name]
to create a list of all email addresses based on all last names. This assumes you intended for all of your variables to be lists.
Even though this will create ten emails (one for each last name) your file will only have one row written to it. This is because zip will stop iteration at the length of the shortest list you pass it. Currently First_Name and Password each contain only one item.
I'm basically guessing since you haven't said anything about what errors you're getting, but the most obvious problem I can see is that you're trying to add a string to a list of tuples, which doesn't make a lot of sense.
'Last_Name' [("#myemail.com")]
should be:
'Last_Name' + "#myemail.com"
Now, as far as what you're actually trying to do, which is extremely unclear, I think you want to use a series of list comprehensions. For example:
users = [i for i in range(0, 10)]
first_names = ["test"+str(user) for user in users]
last_names = ["User%d" %user for user in users]
email_addresses = [last_name + "#myemail.com" for last_name in last_names]
passwords = ["Password1" for user in users]
with open('mycsv.csv', 'wb') as csv_out:
writer = csv.writer(csv_out)
writer.writerows(zip(email_addresses, passwords, first_names, last_names))
output:
User0#myemail.com,Password1,test0,User0
User1#myemail.com,Password1,test1,User1
User2#myemail.com,Password1,test2,User2
User3#myemail.com,Password1,test3,User3
User4#myemail.com,Password1,test4,User4
User5#myemail.com,Password1,test5,User5
User6#myemail.com,Password1,test6,User6
User7#myemail.com,Password1,test7,User7
User8#myemail.com,Password1,test8,User8
User9#myemail.com,Password1,test9,User9
Your zip() will only produce a list w/ 1 item b/c First_Name and Password explicitly each contain only 1 item.
How about this, avoiding the zip entirely:
with open('mycsv.csv', 'wb') as csv_out:
writer = csv.writer(csv_out)
for i in xrange(1,9):
writer.writerow( ["User%d#myemail.com"%i, "Password%d"%i, "test%d"%i, "User%d"%i] )

Categories

Resources