Python 2.7 bottle web - python-2.7

I’m trying to figure out how to rename an existing text file when I change the title of the text file. If I change the title now, it’s going to create a new text file with the new title. The "old text file" that I wanted to save with a new name still exists but with the orginal name. So i got two files with the same content.
I’m creating new articles (text files) through #route('/update/', method='POST') in my ”edit templet” where title=title, text=text. Let’s say after I have created a new article with the name(title) = ”Key” and wrote a bit in that text file. Then If I want to edit/change my ”Key” article I click on that article and present the article in #route('/wiki/',)def show_article(article):. title = article, text = text)
In this template I can change my ”Key” name(title) to ”Lock”. I’m still using the same form #route('/update/', method='POST') to post my changes.
Here is the problem, it creates a new text file instead of renaming the ”Key” article to ”Lock”.
How can I change the #route('/update/', method='POST') to make it realise that I’m working with an already existing text file and only wants to rename that file.
I have tried to use two different method=’POST’ but only gets method not allowed error all the time.
from bottle import route, run, template, request, static_file
from os import listdir
import sys
host='localhost'
#route('/static/<filname>')
def serce_static(filname):
return static_file(filname, root="static")
#route("/")
def list_articles():
files = listdir("wiki")
articles = []
for i in files:
lista = i.split('.')
word = lista[0]
lista1 = word.split('/')
articles.append(lista1[0])
return template("index", articles=articles)
#route('/wiki/<article>',)
def show_article(article):
wikifile = open('wiki/' + article + '.txt', 'r')
text = wikifile.read()
wikifile.close()
return template('page', title = article, text = text)
#route('/edit/')
def edit_form():
return template('edit')
#route('/update/', method='POST')
def update_article():
title = request.forms.title
text = request.forms.text
tx = open('wiki/' + title + '.txt', 'w')
tx.write(text)
tx.close()
return template('thanks', title=title, text=text)
run(host='localhost', port=8080, debug=True, reloader=True)

You can use os.replace('old_name', 'new_name'):
import os
...
tx = open('wiki/' + title + '.txt', 'w')
tx.write(text)
os.replace(tx.name, 'name_you_want.txt') # use os.replace()
tx.close()

Related

Why does one web scraping search return correctly while others return none?

I know this should be simple, which is why it's frustrating. I have searched through questions similar to this, and I do know what "AttributeError: 'NoneType' object has no attribute 'find'" means.
I'm working on a web scraping script which grabs company titles, names, etc. from this website. The confusing this is, two search functions, for company title and for primary contact work perfectly, while any written for company name, email, and phone number do not. Even though all of these exist and the structure is the same as that for the searches that do work. Can anyone point out what's different about these?
My code is here, where the first two searched work but the third returns None. I'm new to python, and any help would be appreciated.
# import libraries
import urllib2
from bs4 import BeautifulSoup
import csv
from datetime import datetime
data = []
for i in range (0, 4):
quote_page = 'http://www.homeopathycenter.org/professional-and-organizational-directory' + '?field_geofield_distance%5Bdistance%5D=50&field_geofield_distance%5Bunit%5D=3959&field_geofield_distance%5Borigin%5D=&field_professional_category_tid=All&combine=&field_address_locality=&field_address_administrative_area=&field_address_country=All&field_consultations_by_phone_value=All&field_consultations_online_value=All&field_animal_consultations_value=All&page={}'.format(i)
page = urllib2.urlopen(quote_page)
# parse the html using beautiful soup and store in variable `soup`
soup = BeautifulSoup(page, 'html.parser')
#identify full contact box
box_array = ['views-row views-row-1 views-row-odd views-row-first',
'views-row views-row-2 views-row-even',
'views-row views-row-3 views-row-odd',
'views-row views-row-4 views-row-even',
'views-row views-row-5 views-row-odd',
'views-row views-row-6 views-row-even',
'views-row views-row-7 views-row-odd',
'views-row views-row-8 views-row-even',
'views-row views-row-9 views-row-odd',
'views-row views-row-10 views-row-even views-row-last']
#loop the boxes
for box in box_array:
full_box = soup.find('div', {'class': box})
#get title from box
title_box = full_box.find('div', {'class': 'views-field views-field-title'})
title_content = title_box.find('span', {'class': 'field-content'})
title = title_content
title = title.text.strip() # strip() is used to remove starting and trailing
#get contact name from box, this works
contact_box = full_box.find('div', {'class': 'views-field views-field-field-primary-contact'})
contact_content = contact_box.find('div', {'class': 'field-content'})
name = contact_content, this works
name = name.text.strip()
#get company name from box, this returns none
company_box = full_box.find('div', {'class': 'views-field views-field-field-company-name'})
company_content = company_box.find('div', {'class': 'field-content'})
company = company_content.text.strip()
EDIT: the error code is as follows:
Traceback (most recent call last):
File "F:\Program Files (x86)\Python\sc3.py", line 46, in
company_content = company_box.find('div', {'class': 'field-content'})
AttributeError: 'NoneType' object has no attribute 'find'

Small web page with Python

I have created a small wiki page with Bottle: Python web framework. Every thing is working fine right now. You create a article by going to "Create a new article" and giving it a Title and write down some Text. Then all of the created article shows on the index page in a list, and you can click on them to open and read.
But the thing is when i click on the article with the purpose to edit the article by giving it a new title and add some new words to the text. It doesn't change the name on the orginal textfile, instead i get a new textfile with the new title and the orginal textfile still remains.
This is the code:
from bottle import route, run, template, request, static_file
from os import listdir
import sys
host='localhost'
#route('/static/<filname>')
def serce_static(filname):
return static_file(filname, root="static")
#route("/")
def list_articles():
'''
This is the home page, which shows a list of links to all articles
in the wiki.
'''
files = listdir("wiki")
articles = []
for i in files:
lista = i.split('.')
word = lista[0]
lista1 = word.split('/')
articles.append(lista1[0])
return template("index", articles=articles)
#route('/wiki/<article>',)
def show_article(article):
'''
Displays the user´s text for the user
'''
wikifile = open('wiki/' + article + '.txt', 'r')
text = wikifile.read()
wikifile.close()
return template('page', title = article, text = text)
#route('/edit/')
def edit_form():
'''
Shows a form which allows the user to input a title and content
for an article. This form should be sent via POST to /update/.
'''
return template('edit')
#route('/update/', method='POST')
def update_article():
'''
Receives page title and contents from a form, and creates/updates a
text file for that page.
'''
title = request.forms.title
text = request.forms.text
tx = open('wiki/' + title + '.txt', 'w')
tx.write(text)
tx.close()
return template('thanks', title=title, text=text)
run(host='localhost', port=8080, debug=True, reloader=True)
The article object is too simple to list, edit or update.
The article should have an ID as its file name.
The article file should have two fields: title and text.
For example: 10022.txt
title:
bottle
text:
Bottle is a fast, simple and lightweight WSGI micro web-framework for Python.
You should retrieve the article by ID.
You can open the file by ID and change its title and text.

How to add url to bookmark?

I am using PyQt4 for creating a custom browser using QtWebKit, but I am stuck on saving bookmarks from the browser. Does anyone know how to achieve that?
You're a little vague on how you want this done, so I'll say we wanted to use a button imported from a UI file called bookmarks_Btn. You'll need to use the pickle module.
Here's the example code...
from PyQt4 import QtCore, QtGui, QtWebKit, uic
import pickle
class window(QtGui.QWidget):
def __init__(self, parent=None):
super(httpWidget, self).__init__(parent)
self.ui = uic.loadUi('mybrowser.ui')
self.ui.setupUi(self)
def bookmarksLoad(self):
print 'Loading bookmarks'
try:
bookOpen = open('bookmarks.txt', 'rb')
bookmarks = pickle.load(bookOpen)
bookOpen.close()
print bookmarks # Not necessary, but for example purposes
# Here you decide how "bookmarks" variable is displayed.
except:
bookOpen = open('bookmarks.txt', 'wb')
bookmarks = 'http://www.stackoverflow.com'
bookWrite = pickle.dump(bookmarks, bookOpen)
bookOpen.close()
print bookmarks # Not necessary, but for example purposes
# Here you decide how "bookmarks" variable is displayed.
QtCore.QObject.connect(self.ui.bookmarks_Btn, QtCore.SIGNAL('clicked()'), self.bookmarksLoad)
self.ui.show()
def bookmarks():
url = input 'Enter a URL: '
bookOpen = open('bookmarks.txt', 'wb')
bookOpen.write(url)
bookOpen.close()
print 'Website bookmarked!'
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
run = window()
bookmarks()
sys.exit(app.exec_())
# You add on here, for example, deleting bookmarks.
However, if you wanted it to be retrieved from an address bar (named address, make the following changes...
# In the bookmarks function...
global url # Add at beginning
# Remove the input line.
# Add at end of __init__ in window class:
url = self.ui.address.text()
global url
That's pretty much the basics. Please note I normally program in Python 3 and PyQt5 so if there are any errors let me know :)

how to not display original picture name in Django

I am building a Django project where users can upload pictures. I am wondering what I should do to not show the original picture name.
I want the url to be something like /pic/randomnumber, and when the picture is downloaded from the website, it would have the name randomnumber.jpg. For example, all the pictures on Tumblr have the name tumblr_blabla.jpg.
I think this is something that should be done in models.py, but I am not quite sure how to implement it.
IMO you should write method save in your model
Something like that:
from PIL import Image
import os
class YOURS_MODEL_NAME(models.Model):
photo = models.ImageField(upload_to="photos")
def save(self, miniature=True):
super(YOURS_MODEL_NAME, self).save()
if miniature:
filepath = self.photo.path
image = Image.open(filepath)
new_filepath = filepath.split('.')
new_filepath = '.'.join("HERE YOU CAN ADD EVERYTHING TO PATH TO THIS PHOTO") + "." + new_filepath[-1].lower()
try:
image.save(new_filepath, quality=90, optimize=1)
except:
image.save(new_filepath, quality=90)
photo_name = self.photo.name.split('.')
photo_name = '.'.join("HERE YOU CAN ADD EVERYTHING YOU WANT TO 'PHOTO NAME'") + "." + photo_name[-1].lower()
self.photo = photo_name
self.save(miniature=False)
# remove old image
os.remove(filepath)
The upload_to argument in your Model definition can be a callable function which you use to customize the name of the file. Taken from the Django docs on
FileField (of which ImageField is a subclass):
upload_to takes two arguments: instance and filename, (where filename is the original filename, which you may also chose to ignore).
Something similar to this in models.py should do the trick:
def random_filename(instance, filename):
file_name = "random_string" # use your choice for generating a random string!
return file_name
class SomeModel(models.Model):
file = models.ImageField(upload_to=random_filename)
(this is similar to the answer this question about FileFields).
If you are going down this path, I would recommend that you use either the hash/checksum or date/time of the file upload. Something along these lines should work (although I haven't tested it myself!):
from hashlib import sha1
def unique_filename(instance, field):
filehash = sha1()
for chunk in getattr(instance, field).chunks():
filehash.update(chunk)
return filehash
class SomeModel(models.Model):
file = models.ImageField(upload_to=unique_filename(field='file'))
Hope this helps!

How to validate contents of a CSV file using Django forms

I have a web app that needs to do the following:
Present a form to request a client side file for CSV import.
Validate the data in the CSV file or ask for another filename.
At one point, I was doing the CSV data validation in the view, after the form.is_valid() call from getting the filename (i.e. I have the imported CSV file into memory in a dictionary using csv.DictReader). After running into problems trying to pass errors back to the original form, I'm now trying to validate the CONTENTS of the CSV file in the form's clean() method.
I'm currently stumped on how to access the in memory file from clean() as the request.FILES object isn't valid. Note that I have no problems presenting the form to the client browser and then manipulating the resulting CSV file. The real issue is how to validate the contents of the CSV file - if I assume the data format is correct I can import it to my models. I'll post my forms.py file to show where I currently am after moving the code from the view to the form:
forms.py
import csv
from django import forms
from io import TextIOWrapper
class CSVImportForm(forms.Form):
filename = forms.FileField(label='Select a CSV file to import:',)
def clean(self):
cleaned_data = super(CSVImportForm, self).clean()
f = TextIOWrapper(request.FILES['filename'].file, encoding='ASCII')
result_csvlist = csv.DictReader(f)
# first line (only) contains additional information about the event
# let's validate that against its form definition
event_info = next(result_csvlist)
f_eventinfo = ResultsForm(event_info)
if not f_eventinfo.is_valid():
raise forms.ValidationError("Error validating 1st line of data (after header) in CSV")
return cleaned_data
class ResultsForm(forms.Form):
RESULT_CHOICES = (('Won', 'Won'),
('Lost', 'Lost'),
('Tie', 'Tie'),
('WonByForfeit', 'WonByForfeit'),
('LostByForfeit', 'LostByForfeit'))
Team1 = forms.CharField(min_length=10, max_length=11)
Team2 = forms.CharField(min_length=10, max_length=11)
Result = forms.ChoiceField(choices=RESULT_CHOICES)
Score = forms.CharField()
Event = forms.CharField()
Venue = forms.CharField()
Date = forms.DateField()
Div = forms.CharField()
Website = forms.URLField(required=False)
TD = forms.CharField(required=False)
I'd love input on what's the "best" method to validate the contents of an uploaded CSV file and present that information back to the client browser!
I assume that when you want to access that file is in this line inside the clean method:
f = TextIOWrapper(request.FILES['filename'].file, encoding='ASCII')
You can't use that line because request doesn't exist but you can access your form's fields so you can try this instead:
f = TextIOWrapper(self.cleaned_data.get('filename'), encoding='ASCII')
Since you have done super.clean in the first line in your method, that should work. Then, if you want to add custom error message to you form you can do it like this:
from django.forms.util import ErrorList
errors = form._errors.setdefault("filename", ErrorList())
errors.append(u"CSV file incorrect")
Hope it helps.