Python/Openpyxl repeating data - python

I am creating a program to move all individual quotes into a single quote matrix, using python and openpyxl, but when trying to load the model information it seems to be repeating the models from the first quote. Each quote is a separate excel file, it is confusing me as I'm having no problem getting the quote information, i.e. name, dates, etc. but the list of models is the same for every quote instead of pulling the models from each quote.
Data Structures:
class Model:
SKU = None
cost = None
qty = None
qty_remaining = None
class Quote:
name = None
number = None
start_date = None
end_date = None
models = []
Function to get data from each quote spreadsheet:
def load_quote(filename):
quote_wb = load_workbook(filename=filename, data_only=True, read_only=True)
quote_sheet = quote_wb.active
quote = Quote()
quote.number = quote_sheet['A2'].value
quote.number = quote.number[9:len(quote.number)]
quote.name = quote_sheet['A3'].value
quote.name = quote.name[14:len(quote.name)]
quote.start_date = quote_sheet['A6'].value
quote.start_date = quote.start_date[14:len(quote.start_date)]
quote.end_date = quote_sheet['A7'].value
quote.end_date = quote.end_date[12:len(quote.end_date)]
for row_vals in quote_sheet.iter_rows(min_row=12, max_row=250, min_col=1, max_col=9, values_only=True):
model = Model()
if row_vals[0] is not None:
model.SKU = row_vals[0]
model.cost = row_vals[4]
model.qty = row_vals[6]
model.qty_remaining = row_vals[8]
quote.models.append(model)
else:
continue
return quote
Each quote is formatted the same, with the quote number being in cell A2, name being in cell A3, start date in cell A4, and end date in cell 5.
From row 12 to 250 is each model in this format:
| SKU | Description | Cost | Original Quantity | Quantity Used | Quantity Remaining |
I do not need the Description or Quantity Used, so I ignore those. If the quote_sheet variable changes based on iterating through each file in a folder, and it is getting all the quote information from each file, why is it not getting the model information from each file?
Not sure if the function call is helpful but here that is as well:
quotes = []
for file in os.listdir(os.chdir(directory)):
if file.endswith(".xlsx"):
quote = load_quote(file)
quotes.append(quote)

All attributes of Quote and Model are defined on the class level - they are class attributes. Class attributes are shared across all instances of a class.
This is not a problem for immutable data like int or str, because by writing:
q1 = Quote()
q1.name = 'quote_name'
you essentially create a new instance attribute name bound to the instance q1 at runtime, which allows the instance to avoid looking up the Quote.name class attribute when you ask for its name.
However, since Quote.models is a mutable data (list), it is shared across all instances of Quote. If you append to that list, this in-place operation will be reflected across any instance of that class.
You can see this behavior with this short example:
class Quote:
name = None
number = None
start_date = None
end_date = None
models = []
q1 = Quote()
q2 = Quote()
q1.models.append('x')
print(q2.models) # output: ['x']
The easiest way to fix this is to turn Quote.models into a proper instance attribute, by defining it in the __init__ method. This way, each Quote instance is created with an unique, independent list attached to its models attribute:
class Quote:
name = None
number = None
start_date = None
end_date = None
def __init__(self):
self.models = []
This should be enough to fix your issue, although I'd really recommend turning all class attributes from Quote and Model into proper instance attributes - for the sake of clarity.

Related

Querying MongoDB by a subclass field

SOLVED:
This works:
print("\nTrying to access by embedded_doc__embedded_int=1:")
for data in Doc.objects(embedded_doc__embedded_int=1):
print(data)
You have to access subclass fields by using the main class variable name (not the class name), followed by __, followed by the subclass variable name, as above.
UPDATE:
My original question is below. I wrote an example to show in condensed and complete form what I'm trying to do.
In this example, I have a Doc class. Each Doc has an Embedded class called "embedded_doc". And within the Embedded class is an integer called "embedded_int".
My goal is to store Docs in MongoDB via MongoEngine, and query the database for Docs that have embedded_doc.embedded_int == 1. So far I haven't been able to figure out how.
class Embedded(EmbeddedDocument):
embedded_int = IntField()
def __eq__(self, other):
return other == self.embedded_int
def __str__(self):
return(str(self.embedded_int))
class Doc(Document):
doc_str = StringField()
embedded_doc = EmbeddedDocumentField(Embedded)
def __str__(self):
return f"{self.doc_str} {str(self.embedded_doc)}"
data1 = Doc(doc_str = "first", embedded_doc = Embedded(embedded_int = 1))
data2 = Doc(doc_str = "second", embedded_doc = Embedded(embedded_int = 2))
#Gives correct output:
#Showing Doc objects in database:
#first 1
#second 2
print("Showing Doc objects in database:")
for data in Doc.objects():
print(data)
#Gives correct output:
#Trying to access by doc_str='first':
#first 1
print("\nTrying to access by doc_str='first':")
for data in Doc.objects(doc_str='first'):
print(data)
#ValueError: The source SON object needs to be of type 'dict' but a '<class 'int'>' was found
#During handling of the above exception, another exception occurred:
#mongoengine.errors.InvalidQueryError: Querying the embedded document 'Embedded' failed, due to an invalid query value
#print("\nTrying to access by embedded_doc=1:")
#for data in Doc.objects(embedded_doc=1):
# print(data)
#SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
#print("\nTrying to access by embedded_doc.embedded_int=1:")
#for data in Doc.objects(embedded_doc.embedded_int=1):
# print(data)
#NameError: name 'embedded_doc' is not defined
#print("\nTrying to access by embedded_doc.embedded_int==1:")
#for data in Doc.objects(embedded_doc.embedded_int==1):
# print(data)
#SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
#print("\nTrying to access by Embedded.embedded_int=1:")
#for data in Doc.objects(Embedded.embedded_int=1):
# print(data)
#Runs, but gives incorrect output:
#Trying to access by Embedded.embedded_int==1:
#first 1
#second 2
print("\nTrying to access by Embedded.embedded_int==1:")
for data in Doc.objects(Embedded.embedded_int==1):
print(data)
ORIGINAL QUESTION:
I'm using Python + MongoDB + MongoEngine to get started with a NoSQL database.
I have a class, Article, which contains a field ArticleMetadata. In turn, ArticleMetadata contains a field called pub_year. I want to query my database for Articles that contain ArticleMetadata with pub_year == 2002. I'm trying this:
for article in Article.objects(ArticleMetadata.pub_year == 2002):
print(article)
input()
But it's printing every article in the database, not just the ones with pub_year == 2002. What do I need to change?
Try ArticleMetadata__pub_year = 2002 instead of ArticleMetadata.pub_year == 2002

Python: Define Object within a Class

Very new to Python and could do with some help. How do I go about referencing members in a class?
I have two csv files. One contains a series of parts and associated material ID. The other is a material index that contains materials ID's and some information about that material.
My intention is to create a third file that contains all of the parts, their material Id's and the information if present in the material index.
I have created a class for the material index and am trying to access objects in this class using material Ids from the part file however, this is not working and I am unsure as to why. Any help is appreciated:
class material():
def __init__(self, name, ftu, e, nu):
self.name = name
self.ftu = ftu
self.e = e
self.nu = nu
def extract_FTU_Strain(input_file_parts,input_file_FTU,output_file):
parts = {}
materials = {}
for aline in open(input_file_FTU, 'r'):
comma_split = aline.strip().split(',')
name = comma_split[1]
ftu = comma_split[8]
e = comma_split[9]
nu = comma_split[7]
try:
materials[int(comma_split[0])] = material(comma_split[1],comma_split[8],comma_split[9],comma_split[7])
#materials[comma_split[0]] = material(comma_split[1],comma_split[8],comma_split[9],comma_split[7])
except:
pass
for i in open(input_file_parts, 'r'):
semicolon_split = i.strip().split(';')
material_id = semicolon_split[3]
part = semicolon_split[0]
part_id = semicolon_split[1]
material_name = materials[material_id].name
FTU = materials[material_id].ftu
Stress = materials[material_id].e
output.write(','.join([part,part_id,material_name,material_id,FTU,Stress]) + '\n')
output = open (output_file,'w')
output.write('Part Title, Part Id, Material Id, FTU, e' + '\n')
output.close()
import sys
input_file_parts = '/parttable.csv'
input_file_FTU = '/Material_Index.csv'
output_file = '/PYTHONTESTING123.csv'
extract_FTU_Strain(input_file_parts,input_file_FTU,output_file)
Since in the comments you said your error is in materials[material_id] make material_id an integer as it was an integer when you created the object.
You created it this way
materials[int(comma_split[0])]=...
But later called it without converting material_id to an int. Do this before calling it in your for loop to write in the output.
material_id = int(material_id)
I may have misinterpreted your question, but going off the line 'How do I go about referencing members in a class?' you can reference member variables like so:
class Demonstration:
def __init__(self, a, b):
self.a = a
self.b = b
def printMembers(self):
print self.a, self.b
So inside the class you can use self.someVariable to reference member variables.
If you want to access them outside of the class:
myclass.myvariable
I'll happily edit the answer if I have't quite understood your question or if there is a specific error you are getting.
I did not understand what error you have, could you put the traceback? Anyway, you are creating a class instance at the time of assignment. For more elegant programming, you could simply do:
m = materials(name, ftu, e, nu)
This way you can access the instance variables like this:
m.name
m.ftu
...
And try, except -> pass it's very dangerous

list of objects python

I am trying to print a list of python objects that contain a list as a property and i am having some unexpected results:
here is my code:
class video(object):
name = ''
url = ''
class topic(object):
topicName = ''
listOfVideo = []
def addVideo(self,videoToAdd):
self.listOfVideo.append(videoToAdd)
def getTopic(self):
return self.topicName
def getListOfVideo(self):
return self.listOfVideo
topic1 = topic()
topic1.topicName = 'topic1'
video1 = video()
video1.name = 'VideoName1'
video1.url = 'VideoURL1'
video2 = video()
video2.name = 'VideoName2'
video2.url = 'VideoURL2'
topic1.addVideo(video1)
topic1.addVideo(video2)
topic2 = topic()
topic2.topicName = 'topic2'
video3 = video()
video3.name = 'VideoName3'
video3.url = 'VideoURL3'
video4 = video()
video4.name = 'VideoName4'
video4.url = 'VideoURL4'
topic2.addVideo(video3)
topic2.addVideo(video4)
topicsList = []
topicsList.append(topic1)
topicsList.append(topic2)
for topicCurrent in topicsList:
print(topicCurrent.topicName)
for video in topicCurrent.getListOfVideo():
print(video.name)
print(video.url)
What I expect to get is this:
topic1
VideoName1
VideoURL1
VideoName2
VideoURL2
topic2
VideoName3
VideoURL3
VideoName4
VideoURL4
but what I actually get is this:
topic1
VideoName1
VideoURL1
VideoName2
VideoURL2
VideoName3
VideoURL3
VideoName4
VideoURL4
topic2
VideoName1
VideoURL1
VideoName2
VideoURL2
VideoName3
VideoURL3
VideoName4
VideoURL4
Why? I want to iterate over my list of topics and print out each video in each topic, but for each topic it prints out all videos???
What is going on here?
You have created class variables instead of instance variables, which are different for each instance object. Define your class as follows:
class topic(object):
def __init__(self):
self.topicName = ''
self.listOfVideo = []
def addVideo(self,videoToAdd):
self.listOfVideo.append(videoToAdd)
def getTopic(self):
return self.topicName
def getListOfVideo(self):
return self.listOfVideo
From Python Tutorial:
Instance variables are for data unique to each instance and class
variables are for attributes and methods shared by all instances of
the class.
EDIT:
One more important thing to consider is that why only listOfVideo was common for all instances but not topicName. It is because list's are mutable objects while string's are immutable.
So any changes made to listOfVideo are common for all instances, i.e., they still refer to listOfVideo defined in the topic namespace.
However when you do topic1.topicName = 'topic1', you create a new variable topicName within topic1 namespace, which overrides the topicName found in topic(class) namespace. You can confirm it by printing the value topic.topicName, which you will find to be an empty string, i.e., ''.

Dynamic keyword with database value

I have a model like this:
class meter1(models.Model):
U1N = models.FloatField(default=0)
U2N = models.FloatField(default=0)
In my view (simplified) I want to set the value for the database dynamically:
def import_data:
dict = {"1":"U1N","2":"U2N",}
c = "1"
q = meter1()
q.eval('dict[c]') = "1"
q.save()
In real dict contains 60 items, and c changes every time in a for loop. The code in this example results in an error: NameError: name 'U1N' is not defined.
How do I dynamically set the keyword for q?
You can also dynamically build a dict from your dict (you shouldn't use a builtin name as identifier BTW) and pass it as kwargs to meter1() (you shouldn't use all-lowers as class names BTW). Oh and yes: you may want to have a look at modelmanager.create() too.
def import_data():
fieldmap = {"1":"U1N","2":"U2N",}
fieldindex = "1"
kw = {fieldmap[fieldindex]: 1.0}
# either:
q = Meter1(**kw)
q.save()
# or:
q = Meter1.objects.create(**kw)
You can use setattr to dynamically set attributes to the objects.

Python: How to determine the type of a property in a class?

I have the following classes defined that inherits from some other classes. Goblin is a Python dependency package that I am extending from.
class AnnotatedVertexProperty(goblin.VertexProperty):
notes = goblin.Property(goblin.String)
datetime = goblin.Property(DateTime)
class KeyProperty(goblin.Property):
def __init__(self, data_type, *, db_name=None, default=None, db_name_factory=None):
super().__init__(data_type, default=None, db_name=None, db_name_factory=None)
class TypedVertex(goblin.Vertex):
def __init__(self):
self.vertex_type = self.__class__.__name__.lower()
super().__init__()
class TypedEdge(goblin.Edge):
def __init__(self):
self.edge_type = self.__class__.__name__.lower()
super().__init__()
class Airport(TypedVertex):
#label
type = goblin.Property(goblin.String)
airport_code = KeyProperty(goblin.String)
airport_city = KeyProperty(goblin.String)
airport_name = goblin.Property(goblin.String)
airport_region = goblin.Property(goblin.String)
airport_runways = goblin.Property(goblin.Integer)
airport_longest_runway = goblin.Property(goblin.Integer)
airport_elev = goblin.Property(goblin.Integer)
airport_country = goblin.Property(goblin.String)
airport_lat = goblin.Property(goblin.Float)
airport_long = goblin.Property(goblin.Float)
At run time, I need to iterate thrown each one of the properties and be able to determine its class type (keyProperty or goblin.Property) I also need to be able to determine if the value is a string, integer, etc...
During instantiation, I create an airport object and set the values as following:
lhr = Airport()
lhr.airport_code = 'LHR'
print (lhr.airport_code.__class__.mro())
lhr.airport_city = 'London'
lhr.airport_name = 'London Heathrow International Airport'
lhr.airport_region = 'UK-EN'
lhr.airport_runways = 3
lhr.airport_longest_runway = 12395
lhr.airport_elev = 1026
lhr.airport_country = 'UK'
lhr.airport_lat = 33.6366996765137
lhr.airport_long = -84.4281005859375
However when I inspect the object while debugging it, all I get is the property name, defined as string and values, defined as string, integer, etc... How can I check for the object type for each property?
Any help or suggestions on how to handle this ?
I figured out what I was looking for. I had to call element.class.dict.items(): and I can get a dictionary with all the properties, mappings, etc...

Categories

Resources