Commits

edanm committed 622d776

Fixed posted answers table, added copy button

  • Participants
  • Parent commits 71a7519

Comments (0)

Files changed (6)

stack2blog/stack2blogapp/stackexchange.py

 try:
 	import json
 except ImportError:
-	import simplejson as json
+	try:
+		import simplejson as json
+	except ImportError:
+		from django.utils import simplejson as json
+
 
 # Site constants
 StackOverflow = 'api.stackoverflow.com'
 #### Hack, because I can't be bothered to fix my mistaking JSON's output for an object not a dict
 # Attrib: Eli Bendersky, http://stackoverflow.com/questions/1305532/convert-python-dict-to-object/1305663#1305663
 class DictObject:
-    def __init__(self, entries): 
+    def __init__(self, entries):
         self.__dict__.update(entries)
 
 class StackExchangeError(Exception):
 	def __new__(cls, items, page, pagesize, build_info):
 		cls.page, cls.pagesize, cls.build_info = page, pagesize, build_info
 		return tuple.__new__(cls, items)
-	
+
 	def reload(self):
 		"""Refreshes the data in the resultset with fresh API data. Note that this doesn't work with extended resultsets."""
 		# kind of a cheat, but oh well
 		new_params[4].update(kw)
 		new_params[4]['page'] = page
 		return new_params[0].build(*new_params[1:])
-	
+
 	def fetch_extended(self, page):
 		"""Returns a new resultset containing data from this resultset AND from the specified page."""
 		next = self.fetch_page(page)
 	def fetch_next(self):
 		"""Returns the resultset of the data in the next page."""
 		return self.fetch_page(self.page + 1)
-	
+
 	def extend_next(self):
 		"""Returns a new resultset containing data from this resultset AND from the next page."""
 		return self.fetch_extended(self.page + 1)
 		self.url = url
 		self.fetch_callback = fetch
 		self.collection = collection if collection != None else self._collection(url)
-	
+
 	def _collection(self, c):
 		return c.split('/')[-1]
 
 			return self.count
 		else:
 			raise NeedsAwokenError(self)
-	
+
 	def fetch(self, **kw):
 		"""Fetch, from the API, the data this sequence is meant to hold."""
 
 				raise ValueError('Supplied fetch callback did not return a usable value.')
 		else:
 			return False
-	
+
 	# Allows the easy creation of updateable, partial classes
 	@classmethod
 	def partial(cls, fetch_callback, site, populate):
 		"""Creates a partial description of the API object, with the proviso that the full set of data can be fetched later."""
 
 		model = cls({}, site, True)
-		
+
 		for k, v in populate.iteritems():
 			setattr(model, k, v)
 
 
 		self.votes = (self.up_vote_count, self.down_vote_count)
 		self.url = 'http://' + self.site.root_domain + '/questions/' + str(self.question_id) + '/' + str(self.id) + '#' + str(self.id)
-	
+
 	def _get_user(s,id):
 		s._owner = self.site.user(id)
 		return s._owner
 		return s._question
 	def _set_quest(s,ob):
 		s._question = ob
-	
+
 	question = property(lambda self: self._question if self._question is not None else self._get_quest(self.question_id), _set_quest)
 	owner = property(lambda self: self._owner if self._owner is not None else self._get_user(self.owner_id), _set_user)
 
 
 		if hasattr(json, 'owner'):
 			self.owner_id = json.owner['user_id']
-	
+
 			owner_dict = json.owner
 			owner_dict['id'] = self.owner_id
 			del owner_dict['user_id']
 			owner_dict['user_type'] = UserType.from_string(owner_dict['user_type'])
-	
+
 			self.owner = User.partial(lambda self: self.site.user(self.id), site, owner_dict)
 
 		self.url = 'http://' + self.site.root_domain + '/questions/' + str(self.id)
 	transfer = ('post_id', 'score', 'edit_count', 'body')
 	def _extend(self, json, site):
 		self.id = json.comment_id
-		
+
 		self.creation_date = datetime.date.fromtimestamp(json.creation_date)
 		self.owner_id = json.owner['owner_id'] if 'owner_id' in json.owner else json.owner['user_id']
 		self.owner = User.partial(lambda self: self.site.user(self.id), site, {
-			'id': self.owner_id, 
+			'id': self.owner_id,
 			'user_type': Enumeration.from_string(json.owner['user_type'], UserType),
 			'display_name': json.owner['display_name'],
 			'reputation': json.owner['reputation'],
 			'email_hash': json.owner['email_hash']})
-		
+
 		if hasattr(json, 'reply_to'):
 			self.reply_to_user_id = json.reply_to['user_id']
 			self.reply_to = User.partial(lambda self: self.site.user(self.id), site, {
 				'display_name': json.reply_to['display_name'],
 				'reputation': json.reply_to['reputation'],
 				'email_hash': json.reply_to['email_hash']})
-		
+
 		self.post_type = PostType.from_string(json.post_type)
 	def get_post(self):
 		if self.post_type == PostType.Question:
 			return self.site.question(self.post_id)
 		elif self.post_type == PostType.Answer:
 			return self.site.answer(self.post_id)
-	
+
 	def __unicode__(self):
 		return u'Comment ' + str(self.id)
 	def __str__(self):
 	def _extend(self, json, site):
 		self.id = json.badge_id
 		self.recipients = StackExchangeLazySequence(User, None, site, json.badges_recipients_url, self._up('recipients'))
-	
+
 	def __str__(self):
 		return self.name
 
 		self.timeline_type = TimelineEventType.from_string(json.timeline_type)
 		self.post_type = PostType.from_string(json.post_type)
 		self.creation_date = datetime.date.fromtimestamp(json.creation_date)
-	
+
 class TimelineEventType(Enumeration):
 	"""Denotes the type of a timeline event."""
 	_map = {'askoranswered': 'AskOrAnswered'}
 		}
 		self.gold_badges, self.silver_badges, self.bronze_badges = self.badge_counts_t
 		self.badge_total = reduce(operator.add, self.badge_counts_t)
-		
+
 		self.url = 'http://' + self.site.root_domain + '/users/' + str(self.id)
-	
+
 	def __unicode__(self):
 		return 'User %d [%s]' % (self.id, self.display_name)
 	def __str__(self):
 		Comment: 'comments/%s',
 		Question: 'questions/%s',
 	}
-	
+
 	def _kw_to_str(self, ob):
 		try:
 			if not isinstance(ob, str):
 			return dump
 		except urllib2.URLError, e:
 			raise StackExchangeError(e)
-	
+
 	def _user_prop(self, qs, typ, coll, kw, prop='user_id'):
 		if prop not in kw:
 			raise LookupError('No user ID provided.')
 			kw['comments'] = str(self.include_comments).lower()
 
 		json = self._request(url, kw)
-		
+
 		if 'page' in json:
 			# we have a paginated resultset
 			page = json['page']
 			pagesize = json['pagesize']
 			items = []
-	
+
 			# create strongly-typed objects from the JSON items
 			for json_item in json[collection]:
 				json_item['_params_'] = kw	# convenient access to the kw hash
 		else:
 			# this isn't a paginated resultset (unlikely, but possible - eg badges)
 			return tuple([typ(x, self) for x in json[collection]])
-	
+
 	def build_from_snippet(self, json, typ):
 		return StackExchangeResultSet([typ(x, self) for x in json])
-	
+
 	def _get(self, typ, ids, coll, kw):
 		root = self.URL_Roots[typ] % ';'.join([str(x) for x in ids])
 		return self.build(root, typ, coll, kw)
 
 		u, = self.users((nid,), **kw)
 		return u
-	
+
 	def users(self, ids, **kw):
 		"""Retrieves a list of the users with the IDs specified in the `ids' parameter."""
 		return self._get(User, ids, 'users', kw)
 
 		a, = self.answers((nid,), **kw)
 		return a
-	
+
 	def answers(self, ids=None, **kw):
 		"""Retrieves a set of the answers with the IDs specified in the 'ids' parameter, or by the
 		user_id specified."""
 		"""Retrieves an object representing a comment with the ID `nid`."""
 		c, = self.comments((nid,), **kw)
 		return c
-	
+
 	def comments(self, ids=None, **kw):
 		"""Retrieves a set of the comments with the IDs specified in the 'ids' parameter."""
 		if ids == None:
 			return self._user_prop('comments', Comment, 'comments', kw)
 		else:
 			return self._get(Comment, ids, 'comments', kw)
-	
+
 	def question(self, nid, **kw):
 		"""Retrieves an object representing a question with the ID `nid`. Note that an answer ID can not be specified -
 unlike on the actual site, you will receive an error rather than a redirect to the actual question."""
 		q, = self.questions((nid,), **kw)
 		return q
-	
+
 	def questions(self, ids=None, **kw):
 		"""Retrieves a set of the comments with the IDs specified in the 'ids' parameter."""
 		if 'answers' not in kw:
 			return self._user_prop('questions', Question, 'questions', kw)
 		else:
 			return self._get(Question, ids, 'questions', kw)
-	
+
 	def recent_questions(self, **kw):
 		"""Returns the set of the most recent questions on the site, by last activity."""
 		if 'answers' not in kw:
 			kw['answers'] = 'true'
 		return self.build('questions', Question, 'questions', kw)
-	
+
 	def users_with_badge(self, bid, **kw):
 		"""Returns the set of all the users who have been awarded the badge with the ID 'bid'."""
 		return self.build('badges/' + str(bid), User, 'users', kw)
-	
+
 	def all_badges(self, **kw):
 		"""Returns the set of all the badges which can be awarded on the site, excluding those which are awarded for specific tags."""
 		return self.build('badges', Badge, 'badges', kw)
-	
+
 	def badges(self, ids=None, **kw):
 		"""Returns the users with the badges with IDs."""
 		if ids == None:
 		"""Returns an object representing the badge with the ID 'nid'."""
 		b, = self.badges((nid,), kw)
 		return b
-	
+
 	def all_tag_badges(self, **kw):
 		"""Returns the set of all the tag-based badges: those which are awarded for performance on a specific tag."""
 		return self.build('badges/tags', Badge, 'badges', kw)
-	
+
 	def all_tags(self, **kw):
 		return self.build('tags', Tag, 'tags', kw)
-	
+
 	def stats(self, **kw):
 		return self.build('stats', Statistics, 'statistics', kw)[0]

stack2blog/stack2blogapp/static_media/js/stack2blog.js

 
 // Perform event initialization, etc., only after the DOM is complete.
 $(function() {
-
+	$("#select_all_button").click(function() {
+		$(".answer_body").select();
+	});
 });

stack2blog/stack2blogapp/static_media/style.css

 	color: #0077CC;
 }
 
+#userpage_container .sort_help {
+	text-decoration:none;
+}
+
 #userpage_container .answer_date {
 	color:black;
 	font-size:16px;
 	color: #0077CC;
 }
 
+#userpage_container .already_published_count {
+	font-size:14px;
+
+}
+
 /***********************
  Postanswer page stylings
  ***********************/
 	background:#b6c8e9;
 	overflow:auto;
 	border:1px solid black;
-
+	min-height: 600px;
 }
 
 #postanswer_container .mark_as_posted {

stack2blog/stack2blogapp/templates/post_answer.html

 	<div class="clear"></div>
 
 	<p class="grid_12">
-	To post this answer, simply copy the following code into wordpress (in the html tab).
+	To post this answer, simply copy the following code into Wordpress' htlm tab.
 	</p>
 	<div class="clear"></div>
 
-	<div class="grid_12 answer_body">
-		<pre>{{answer.body}}</pre>
-	</div>
-	<div class="clear"></div>
-
 	<div class="mark_as_posted">
-		<div class="grid_4">
+		<div class="grid_3">
 
 			<a href="/perform_post/{{answer.id}}">
 				<img src="{{MEDIA_URL}}stack2blog_media/static_media/img/mark_as_posted.jpg" alt="Mark as Posted" />
 			This will make the answer show up in the posted table on your userpage.
 			</P>
 			<p>
-			Use this after you've actually posted this answer.
+			Use this after you've posted this answer to your blog.
 			</p>
 		</div>
 	</div>
+
+	<div id="select_all_button" class="grid_12"><button>Select All</button></div>
+	<div class="clear"></div>
+
+	<textarea class="grid_12 answer_body">{{answer.body}}</textarea>
+	<div class="clear"></div>
+
+
 </div>
 
 {% endblock %}

stack2blog/stack2blogapp/templates/userpage.html

 	<h1 class="grid_12" class="display_name"> Welcome {{so_user_display_name}} </h1>
 	<div class="clear"></div>
 
-	<p class="grid_12">
-	Here you can see which answers you've already published, and which are waiting for you to post.
+	<p class="grid_12 already_published_count">
+		{% if published_answers|length %}
+			You have <a href="#published_answers_table"><strong>published {{published_answers|length}} answer{{published_answers|pluralize}}</strong></a> already (see table below).
+		{%else%}
+			You haven't <a href="#published_answers_table"><strong>published any answers</strong></a> yet. Why not try publishing one now? (See table below for published answers).
+		{% endif %}
 	</p>
 	<div class="clear"></div>
 
 
 	<!-- The headers -->
 	<ul class="answers_table_header unpublished_answers_table_header">
-		<li class="grid_1"><a href="?length_filter={{length_filter|urlencode}}&amp;search_sort=votes&amp;search_order={{search_order_post_votes}}">Votes</a></li>
-		<li class="grid_4"><a href="?length_filter={{length_filter|urlencode}}&amp;search_sort=question&amp;search_order={{search_order_post_title}}">Title</a> <span class="sort_help">(sort by activity)</span></li>
-		<li class="grid_6"><a href="?length_filter={{length_filter|urlencode}}&amp;search_sort=answer&amp;search_order={{search_order_post_answer}}">Answer</a> <span class="sort_help">(sort by length)</span></li>
+		<li class="grid_2"><a href="?length_filter={{length_filter|urlencode}}&amp;search_sort=votes&amp;search_order={{search_order_post_votes}}">Votes</a></li>
+		<li class="grid_4">
+			<a href="?length_filter={{length_filter|urlencode}}&amp;search_sort=question&amp;search_order={{search_order_post_title}}">Title</a>
+			<a class="sort_help" href="?length_filter={{length_filter|urlencode}}&amp;search_sort=question&amp;search_order={{search_order_post_title}}"><span class="sort_help"> (sort by activity)</span></a>
+		</li>
+		<li class="grid_6">
+			<a href="?length_filter={{length_filter|urlencode}}&amp;search_sort=answer&amp;search_order={{search_order_post_answer}}">Answer</a>
+			<a class="sort_help" href="?length_filter={{length_filter|urlencode}}&amp;search_sort=answer&amp;search_order={{search_order_post_answer}}"><span class="sort_help">(sort by length)</span></a>
+		</li>
 		<div class="clear"></div>
 	</ul>
 
 	{% for answer in unpublished_answers %}
 		<div class="answer unposted_answer">
-			<div class="grid_1 votecount"> {{answer.score}} </div>
+			<div class="grid_2 votecount"> {{answer.score}} </div>
 			<div class="grid_4 question_title">
 				<a class="question_link" href="{{answer.url}}"><div class="answer_title">{{answer.title}}</div>
 				</a>
-				<div class="answer_date">Last Activity: {{answer.last_activity_date}}</div>
+				<div class="answer_date">Last Activity: {{answer.last_activity_date|date:"F j, Y"}}</div>
 			</div>
 			<div class="grid_6"> <div class="answer_length">Length: {{answer.body_blurb|length}} characters</div> <div class="answer_body">{{answer.body_blurb|safe}}</div> </div>
 			<div class="post_button"> <a href="/answer/{{answer.id}}/"> <img src="{{MEDIA_URL}}stack2blog_media/static_media/img/pos.jpg" alt="Post to Blog" /></a> </div>
 
 
 	<!-- Published answers -->
-	<h2 class="grid_12 published_answers_header">Answers you've already published ({{ published_answers|length }}):</h2>
+	<h2 id="published_answers_table" class="grid_12 published_answers_header">Answers you've already published ({{ published_answers|length }}):</h2>
 	<div class="clear"></div>
 
 	<!-- The headers -->
 	<ul class="answers_table_header published_answers_table_header">
-		<li class="grid_1">Votes</li>
-		<li class="grid_6">Question Title</li>
-		<li class="grid_5">Answer</li>
+		<div class="grid_2">Publication date</div>
+		<li class="grid_2">Votes</li>
+		<li class="grid_4">Question Title</li>
+		<li class="grid_6">Answer</li>
 		<div class="clear"></div>
 	</ul>
 
 	{% for answer in published_answers %}
-		<div class="answer unposted_answer">
-			<div class="grid_1 votecount"> {{answer.score}} </div>
-			<div class="grid_3 question_title"> {{answer.title}} </div>
-			<div class="grid_5 answer_body"> {{answer.body_blurb|safe}} </div>
-
+		<div class="answer posted_answer">
+			<div class="grid_2">{{answer.publication_date|date:"F j, Y"}}</div>
+			<div class="grid_2 votecount"> {{answer.score}} </div>
+			<div class="grid_4 question_title">
+				<a class="question_link" href="{{answer.url}}"><div class="answer_title">{{answer.title}}</div>
+				</a>
+				<div class="answer_date">Last Activity: {{answer.last_activity_date}}</div>
+			</div>
+			<div class="grid_6"> <div class="answer_length">Length: {{answer.body_blurb|length}} characters</div> <div class="answer_body">{{answer.body_blurb|safe}}</div> </div>
 			<div class="clear"></div>
 		</div>
 
 	{% endfor %}
 
-	<div class="clear"></div>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
+	<br/>
 
 </div>
 

stack2blog/stack2blogapp/views.py

 	published_answers = [blurbifyAnswer(answer) for answer in answers if PublishedPost.objects.filter(postid = answer.id).count() > 0]
 	unpublished_answers = [blurbifyAnswer(answer) for answer in answers if PublishedPost.objects.filter(postid = answer.id).count() == 0]
 
+	for answer in published_answers:
+		answer.publication_date = PublishedPost.objects.get(postid = answer.id).publication_date
+
+	# Sort published answers by date.
+	published_answers.sort(key=lambda ans: ans.publication_date, reverse=True)
+
 	len_unpublished = len(unpublished_answers)
 
 	# Get filter and sorting options.
 	if search_order not in ('asc', 'dec'):
 		search_order = 'dec'
 
-	# Apply the filters to the published answers.
+
+
+	# Apply the filters to the unpublished answers.
 	unpublished_answers = [answer for answer in unpublished_answers if len(answer.body) > length_filter]
 
 	num_answers_remaining = len_unpublished - len(unpublished_answers)