OpenPyXl how to format cell as date - python

I have a problem with a report I am reading with openpyxl. Sometimes cell formats in a file I receive are not correct, and instead of "10.03.2020" (cell format Date) I get "43900,88455" (cell format General).
I tried google, openpyxl documentation and StackOverflow but that did not bring me any closer to the solution. Would you be able to help and advise how to switch cell format to Short Date, please?
Below did not work, I tried many other ideas but still in a dead end. I need correct dates for other operations within this script.
def sanitizeDates(self):
# pass
for i in range(3, self.fileLength-1):
self.mainWs.cell(i, 4).number_format = 'dd/mm/yyyy'
self.mainWs.cell(i, 16).number_format = 'dd/mm/yyyy'
Copy comment: So I have tried
print("Cell (4) type is: " + str(type(self.mainWs.cell(i, 4).value)) + " and current value is " + str(self.mainWs.cell(i, 4).value))
print("Cell (16) type is: " + str(type(self.mainWs.cell(i, 16).value)) + " and current value is " + str(self.mainWs.cell(i, 16).value))
that results in Cell (4) type is: <class 'datetime.datetime'>
and current value is 2020-03-10 22:41:41
Cell (16) type is: <class 'float'> and current value is 43900.9475
Excel displays it as "General" = "43900,88455"

So, I have figured this out after all. #stovfl thanks for your hints, this brought me to an idea what to look for and as a result, how to solve my issue.
Original thread on StackOverflow is available here: https://stackoverflow.com/a/29387450/5210159
Below is a working code:
def sanitizeDates(self):
for i in range(3, self.fileLength - 1):
# Check which cells should be sanitized and proceed
if isinstance(self.mainWs.cell(i, 4).value, float) and isinstance(self.mainWs.cell(i, 16).value, float):
print(str(self.mainWs.cell(i, 4).value) + " / " + str(self.mainWs.cell(i, 16).value) + " -> " + str(self.convertDate(self.mainWs.cell(i, 4).value)) + " / " + self.convertDate(self.mainWs.cell(i, 16).value))
self.mainWs.cell(i, 4).value = self.convertDate(self.mainWs.cell(i, 4).value)
self.mainWs.cell(i, 16).value = self.convertDate(self.mainWs.cell(i, 16).value)
elif isinstance(self.mainWs.cell(i, 4).value, float):
print(str(self.mainWs.cell(i, 4).value) + " -> " + str(self.convertDate(self.mainWs.cell(i, 4).value)))
self.mainWs.cell(i, 4).value = self.convertDate(self.mainWs.cell(i, 4).value)
elif isinstance(self.mainWs.cell(i, 16).value, float):
print(str(self.mainWs.cell(i, 16).value) + " -> " + str(self.convertDate(self.mainWs.cell(i, 16).value)))
self.mainWs.cell(i, 16).value = self.convertDate(self.mainWs.cell(i, 16).value)
def convertDate(self, dateToConvert):
# Thank you, StackOverflow <3
# https://stackoverflow.com/questions/29387137/how-to-convert-a-given-ordinal-number-from-excel-to-a-date
epochStart = datetime(1899, 12, 31)
if dateToConvert >= 60:
dateToConvert -= 1 # Excel leap year bug, 1900 is not a leap year
return epochStart + timedelta(days=dateToConvert)
After execution I get following results:
43899.89134259259 -> 2020-03-09 21:23:32
43900.9475 -> 2020-03-10 22:44:24

Related

How to Handle Exceptions Caused by Holidays and Weekends in Python

I'm using an API to lookup historical stock market prices for a given company on the last day of each month. The problem is that the last day can sometimes fall on a weekend or holiday, in which case the API returns a KeyError. I've tried using an exception to handle this by adding n number to the date to get the next-closest valid one, but this is not foolproof.
Here is my existing code:
import os
from iexfinance.stocks import get_historical_data
import iexfinance
import pandas as pd
# Set API Keys
os.environ['IEX_API_VERSION'] = 'iexcloud-sandbox'
os.environ['IEX_TOKEN'] = 'Tsk_5798c0ab124d49639bb1575b322841c4'
stocks = ['AMZN', 'FDX', 'XXXXX', 'BAC', 'COST']
date = "20191130"
for stock in stocks:
try:
price_df = get_historical_data(stock, date, close_only=True,output_format='pandas')
price = price_df['close'].values[0]
print(price)
except KeyError:
date = str(int(date) - 1)
price_df = get_historical_data(stock, date, close_only=True, output_format='pandas')
price = price_df['close'].values[0]
print(price)
except iexfinance.utils.exceptions.IEXQueryError:
print(stock + " is not a valid company")
But if you change date = "20160131", then you get a KeyError again.
So is there a simple way to handle these exceptions and get the next-valid date?
Note that the API Key is public and for sandbox purposes, so feel free to use
I think this might work:
def get_prices(stocks, date):
for stock in stocks:
try:
price_df = get_historical_data(stock, date, close_only=True,output_format='pandas')
price = price_df['close'].values[0]
print(stock + " was # $" + str(price) + " on " + str(date))
except KeyError:
return get_prices(stocks, date = str(int(date) - 1))
print(stock + " was # $" + str(price) + " on " + str(date))
except iexfinance.utils.exceptions.IEXQueryError:
print(stock + " is not a valid company")

SPSS/Python - Accessing variable labels

I am trying to access variable labels in for loop in SPSS using Python. The for loop iterates over a range of variables, deleting 1-3 and renaming 4 and 5 in a sequence of 5 variables. This works fine, but now when trying to access the variable labels via SPSS I am running into the 'unicode object has no attribute keyes' error.
I recognize that I need to somehow refer to the key instead of the string in my array, but as a novice programmer I am struggling to figure out how to update my existing code:
begin program.
import spss, spssaux
vdict=spssaux.VariableDict()
mylist=vdict.range(start="M10", end="ENDOK_D")
nvars = len(mylist)
mycounter = 1
durations = ""
for i in range(nvars):
myvar = mylist[i]
if (mycounter < 4):
spss.Submit("delete variables %s." % myvar)
mycounter +=1
elif (mycounter == 4):
varlabel = mylist[i].VariableLabel
spss.Submit('variable labels %s "%s" [TimeStamp]' % (myvar,varlabel) + ".")
if (myvar.endswith("_C")): mynewvar = myvar[:-2] + "_TS"
spss.Submit("rename variables (%s = %s)" % (myvar,mynewvar) + ".")
spss.Submit("formats %s (DATETIME28.4)" % (mynewvar) + ".")
mycounter +=1
elif (mycounter == 5):
varlabel = mylist[i].VariableLabel
spss.Submit('variable labels %s "%s" [TimeStamp]' % (myvar,varlabel) + ".")
if (myvar.endswith("_D")): mynewvar = myvar[:-2] + "_TSD"
spss.Submit("rename variables (%s = %s)" % (myvar,mynewvar) + ".")
durations += mynewvar + " "
mycounter = 1
spss.Submit("alter type %s (F4.0)" % durations + ".")
end program.
Any help would be greatly appreciated.
The line
spss.Submit('variable labels %s "%s" [TimeStamp]' % (myvar,varlabel) + ".") is what's causing you trouble.
The [TimeStamp] bit is basically telling python to look for a key named "TimeStamp" in a dictionary but before the [TimeStamp] it doesn't find the right data structure - namely a dictionary - but a string which does not have keys. Maybe this might make it clearer:
myDict = {
"varname": "myVariable",
"label": "This is myVariable's label!",
"TimeStamp": "20190204-0814"
}
print (myDict["varname"])
print (myDict["TimeStamp"])
>>> myVariable
>>> 20190204-0814
Python is looking for a data structure like this and wants to look up the key "TimeStamp". Now, I assume you just want your labels to read "Whatever Variable Label was assigned [TimeStamp]"?
Simply change the two lines to
spss.Submit('variable labels %s "%s" + " [TimeStamp]"' % (myvar,varlabel) + ".")

Working code but pycharm marks tmF and tmR in list as "local variable referenced before assignment", why is this?

I've only recently started programming so there may be a simple answer to this question but I couldn't find it on here. My code works fine for what I want to do but as I am new to this I want to get into the practice of writing good readable code. I am using PyCharm and I noticed it marked some of the code below as not defined. I understand a little about global vs local variables and I guess it has something to do with this but I can't work out why this part of the code works at all if this is the case.
if len(primerF) < 14:
tmF = (no_A_F + no_T_F) * 2 + (no_C_F + no_G_F) * 4
float_tmF = float(tmF)
print("Forward primer tm: " + str(float_tmF))
elif len(primerR) > 13:
tmR = 64.9 + 41*(no_C_R + no_G_R - 16.4) / (no_A_R + no_T_R + no_G_R +
no_C_R)
print("Reverse primer tm: " + str(tmR))
if len(primerR) < 14:
tmR = (no_A_R + no_T_R) * 2 + (no_C_R + no_G_R) * 4
print("Reverse primer tm: " + str(tmR))
elif len(primerR) > 13:
tmR = 64.9 + 41*(no_C_R + no_G_R - 16.4) / (no_A_R + no_T_R + no_G_R +
no_C_R)
print("Reverse primer tm: " + str(tmR))
gc_F = (no_G_F + no_C_F)
gc_content_F = float(gc_F) / forward_length * 100
print("Forward GC content: " + str(gc_content_F) + "%")
gc__R = (no_G_R + no_C_R)
gc_content_R = float(gc__R) / reverse_length * 100
print("Reverse GC content: " + str(gc_content_R) + "%")
This block here is marked as name not defined and if I click on the bubble it says: "This inspection warns about local variables referenced before assignment".
list_tm = [**tmF**, **tmR**]
high_tm = max(list_tm)
low_tm = min(list_tm)
You're setting tmF and tmR in conditional blocks that do not cover all possibilities. For example, if primerF is >= 14, tmF will never be set before the list_tm = [tmF, tmR] statement. This will result in a runtime error, which pycharm is warning you about.
One way to fix this is to set default values of tmF and tmR at the top of your code, or to have full conditional coverage by setting tmF and tmR in else (not just elif) blocks.

Dictionary query

Below is a function that extracts information from a database which holds information about events. Everything works except that when I try and iterate through times in rows in HTML it is apparently empty. I will therefore assume that rows.append(time) is not doing what it should be doing. I tried rows.append((time)) and that did not work either.
def extractor(n):
date = (datetime.datetime.now() + datetime.timedelta(days=n)).date()
rows = db.execute("SELECT * FROM events WHERE date LIKE :date ORDER BY date", date = str(date) + '%')
printed_day = date.strftime('%A') + ", " + date.strftime('%B') + " " + str(date.day) + ", " + str(datetime.datetime.now().year)
start_time = time.strftime("%H:%M:%S")
for row in rows:
date_split = str.split(row['date'])
just_time = date_split[1]
if just_time == '00:00:00':
just_time = 'All Day'
else:
just_time = just_time[0:5]
times.append((just_time))
rows.append(times)
results.append((rows, printed_day, start_time, times))
Solved it:
replace
times.append((just_time))
rows.append(times)
with
row['times'] = just_time

Geotagging JPEGs with pyexiv2

I am geotagging JPEGs using the pyexiv2 Python module using code I found in another SO answer (see: What is the best way to geotag jpeg-images using Python?) and I have a question about the GPSTag value.
The code given in the answer has the following lines:
exiv_image["Exif.Image.GPSTag"] = 654
exiv_image["Exif.GPSInfo.GPSMapDatum"] = "WGS-84"
exiv_image["Exif.GPSInfo.GPSVersionID"] = '2 0 0 0'
I have looked at the Exiv2 documentation and found descriptions of GPSTag, GPSMapDatum, and GPSVersionID but am still confused about the value for GPSTag.
From the documentation it says:
A pointer to the GPS Info IFD. The Interoperability structure of the GPS Info IFD, like that of Exif IFD, has no image data.
This description does not really explain how to determine what value to use and I have not been able to find a better description of GPSTag online.
So my questions are:
Given a new image, how do you determine the value of Exif.Image.GPSTag?
Why is the code sample using a value of 654 (this may be answered by question one)?
Thanks for your help.
Best way to geotag photos using pyexiv2 is definitely with my program, GottenGeography ;-)
But seriously though, if you're wanting to access GPS data from pyexiv2, that code looks like this:
GPS = 'Exif.GPSInfo.GPS'
try:
self.latitude = dms_to_decimal(
*self.exif[GPS + 'Latitude'].value +
[self.exif[GPS + 'LatitudeRef'].value]
)
self.longitude = dms_to_decimal(
*self.exif[GPS + 'Longitude'].value +
[self.exif[GPS + 'LongitudeRef'].value]
)
except KeyError:
pass
try:
self.altitude = float(self.exif[GPS + 'Altitude'].value)
if int(self.exif[GPS + 'AltitudeRef'].value) > 0:
self.altitude *= -1
except KeyError:
pass
And writing looks like this:
self.exif[GPS + 'AltitudeRef'] = '0' if self.altitude >= 0 else '1'
self.exif[GPS + 'Altitude'] = Fraction(self.altitude)
self.exif[GPS + 'Latitude'] = decimal_to_dms(self.latitude)
self.exif[GPS + 'LatitudeRef'] = 'N' if self.latitude >= 0 else 'S'
self.exif[GPS + 'Longitude'] = decimal_to_dms(self.longitude)
self.exif[GPS + 'LongitudeRef'] = 'E' if self.longitude >= 0 else 'W'
self.exif[GPS + 'MapDatum'] = 'WGS-84'
With these support functions:
class Fraction(fractions.Fraction):
"""Only create Fractions from floats.
>>> Fraction(0.3)
Fraction(3, 10)
>>> Fraction(1.1)
Fraction(11, 10)
"""
def __new__(cls, value, ignore=None):
"""Should be compatible with Python 2.6, though untested."""
return fractions.Fraction.from_float(value).limit_denominator(99999)
def dms_to_decimal(degrees, minutes, seconds, sign=' '):
"""Convert degrees, minutes, seconds into decimal degrees.
>>> dms_to_decimal(10, 10, 10)
10.169444444444444
>>> dms_to_decimal(8, 9, 10, 'S')
-8.152777777777779
"""
return (-1 if sign[0] in 'SWsw' else 1) * (
float(degrees) +
float(minutes) / 60 +
float(seconds) / 3600
)
def decimal_to_dms(decimal):
"""Convert decimal degrees into degrees, minutes, seconds.
>>> decimal_to_dms(50.445891)
[Fraction(50, 1), Fraction(26, 1), Fraction(113019, 2500)]
>>> decimal_to_dms(-125.976893)
[Fraction(125, 1), Fraction(58, 1), Fraction(92037, 2500)]
"""
remainder, degrees = math.modf(abs(decimal))
remainder, minutes = math.modf(remainder * 60)
return [Fraction(n) for n in (degrees, minutes, remainder * 60)]
Although I am currently working on an alternative to pyexiv2 that uses GObject introspection to access the exiv2 library much more directly, called GExiv2, and would love to have some feedback on it. Both gexiv2 and pyexiv2 are wrappers around the same exiv2 library, but the difference is that pyexiv2 is a very large project with lots of glue, only works in python, and is on the brink of abandonment*; whereas gexiv2 is light and nimble, accessible from any programming language, and is well maintained thanks to it's use by Shotwell.
Hope this helps!
* pyexiv2's author, Olivier Tilloy, has asked me for help with maintainership as he no longer has much time
My version, a little lengthy...
from fractions import Fraction
import pyexiv2
try:
metadata = pyexiv2.metadata.ImageMetadata(image_file)
metadata.read();
thumb = metadata.exif_thumbnail
try:
latitude = metadata.__getitem__("Exif.GPSInfo.GPSLatitude")
latitudeRef = metadata.__getitem__("Exif.GPSInfo.GPSLatitudeRef")
longitude = metadata.__getitem__("Exif.GPSInfo.GPSLongitude")
longitudeRef = metadata.__getitem__("Exif.GPSInfo.GPSLongitudeRef")
latitude = str(latitude).split("=")[1][1:-1].split(" ");
latitude = map(lambda f: str(float(Fraction(f))), latitude)
latitude = latitude[0] + u"\u00b0" + latitude[1] + "'" + latitude[2] + '"' + " " + str(latitudeRef).split("=")[1][1:-1]
longitude = str(longitude).split("=")[1][1:-1].split(" ");
longitude = map(lambda f: str(float(Fraction(f))), longitude)
longitude = longitude[0] + u"\u00b0" + longitude[1] + "'" + longitude[2] + '"' + " " + str(longitudeRef).split("=")[1][1:-1]
latitude_value = dms_to_decimal(*metadata.__getitem__("Exif.GPSInfo.GPSLatitude").value + [metadata.__getitem__("Exif.GPSInfo.GPSLatitudeRef").value]);
longitude_value = dms_to_decimal(*metadata.__getitem__("Exif.GPSInfo.GPSLongitude").value + [metadata.__getitem__("Exif.GPSInfo.GPSLongitudeRef").value]);
print "--- GPS ---"
print "Coordinates: " + latitude + ", " + longitude
print "Coordinates: " + str(latitude_value) + ", " + str(longitude_value)
print "--- GPS ---"
except Exception, e:
print "No GPS Information!"
#print e
# Check for thumbnail
if(thumb.data == ""):
print "No thumbnail!"
except Exception, e:
print "Error processing image..."
print e;

Categories