<commit_before>
import discord
from discord.ext import commands

import datetime
import pyowm.exceptions

import clients
import credentials
from utilities import checks

def setup(bot):
	bot.add_cog(Location(bot))

class Location:

	'''
	Commands regarding locations
	Also see Random cog for random location commands
	'''

	def __init__(self, bot):
		self.bot = bot

	# TODO: handle random location command

	@commands.command()
	@checks.not_forbidden()
	async def country(self, ctx, *, country : str):
		'''Information about a country'''
		# TODO: subcommands for other options to search by (e.g. capital)
		async with clients.aiohttp_session.get("https://restcountries.eu/rest/v2/name/{}".format(country)) as resp:
			if resp.status == 400:
				await ctx.embed_reply(":no_entry: Error")
				return
			data = await resp.json()
			if resp.status == 404:
				await ctx.embed_reply(":no_entry: Error: {}".format(data.get("message")))
				return
		country_data = {}
		for _country in data:
			if _country["name"].lower() == country.lower() or country.lower() in [n.lower() for n in _country["altSpellings"]]:
				country_data = _country
				break
		if not country_data: country_data = data[0]
		# Flag, Population, Area
		fields = [("Flag", "[:flag_{}:]({})".format(country_data["alpha2Code"].lower(), country_data["flag"])), ("Population", "{:,}".format(country_data["population"])), ("Area", "{:,} sq km".format(country_data["area"]))]
		# Capital
		if country_data["capital"]: fields.append(("Capital", country_data["capital"]))
		# Languages
		languages = ["{}\n{}".format(l["name"], " ({})".format(l["nativeName"]) if l["nativeName"] != l["name"] else "") for l in country_data["languages"]]
		if len(languages) == 1 and len(languages[0]) > 22: languages = languages[0] # 22: embed field value limit without offset
		else: languages = '\n'.join(l.replace('\n', ' ') for l in languages)
		fields.append((clients.inflect_engine.plural("Language", len(country_data["languages"])), languages))
		# Currencies
		currencies = ["{0[name]}\n({0[code]}{1})".format(c, ", " + c["symbol"] if c["symbol"] else "") for c in country_data["currencies"]]
		if len(currencies) == 1 and len(currencies[0]) > 22: currencies = currencies[0] # 22: embed field value limit without offset
		else: currencies = '\n'.join(c.replace('\n', ' ') for c in currencies)
		fields.append((clients.inflect_engine.plural("currency", len(country_data["currencies"])).capitalize(), currencies))
		# Regions/Subregions
		if country_data["subregion"]: fields.append(("Region: Subregion", "{0[region]}:\n{0[subregion]}".format(country_data)))
		else: fields.append(("Region", country_data["region"]))
		# Regional Blocs
		if country_data["regionalBlocs"]:
			regional_blocs = ["{0[name]}\n({0[acronym]})".format(rb) for rb in country_data["regionalBlocs"]]
			if len(regional_blocs) == 1: regional_blocs = regional_blocs[0]
			else: regional_blocs = '\n'.join(rb.replace('\n', ' ') for rb in regional_blocs)
			fields.append((clients.inflect_engine.plural("Regional Bloc", len(country_data["regionalBlocs"])), regional_blocs))
		# Borders
		if country_data["borders"]:
			fields.append((clients.inflect_engine.plural("Border", len(country_data["borders"])), '\n'.join(", ".join(country_data["borders"][i:i + 4]) for i in range(0, len(country_data["borders"]), 4))))
		# Timezones
		fields.append((clients.inflect_engine.plural("Timezone", len(country_data["timezones"])), '\n'.join(", ".join(country_data["timezones"][i:i + 2]) for i in range(0, len(country_data["timezones"]), 2))))
		# Demonym
		if country_data["demonym"]: fields.append(("Demonym", country_data["demonym"]))
		# Top-Level Domains
		if country_data["topLevelDomain"][0]:
			fields.append((clients.inflect_engine.plural("Top-Level Domain", len(country_data["topLevelDomain"])), ", ".join(country_data["topLevelDomain"])))
		# Calling Codes
		if country_data["callingCodes"] and country_data["callingCodes"][0]:
			fields.append((clients.inflect_engine.plural("Calling Code", len(country_data["callingCodes"])), ", ".join('+' + cc for cc in country_data["callingCodes"])))
		await ctx.embed_reply(title = "{}{}".format(country_data["name"], " ({})".format(country_data["nativeName"]) if country_data["nativeName"] not in country_data["name"] else ""), fields = fields)

	@commands.group(invoke_without_command = True)
	@checks.not_forbidden()
	async def geocode(self, ctx, *, address : str):
		'''Convert addresses to geographic coordinates'''
		async with clients.aiohttp_session.get("https://maps.googleapis.com/maps/api/geocode/json", params = {"address": address, "key": credentials.google_apikey}) as resp:
			data = await resp.json()
		if data["status"] == "ZERO_RESULTS":
			await ctx.embed_reply(":no_entry: Address/Location not found")
			return
		if data["status"] != "OK":
			await ctx.embed_reply(":no_entry: Error")
			return
		data = data["results"][0]
		await ctx.embed_reply(title = "Geographic Coordinates for " + data["formatted_address"], fields = (("Latitude", data["geometry"]["location"]["lat"]), ("Longitude", data["geometry"]["location"]["lng"])))

	@geocode.command(name = "reverse")
	@checks.not_forbidden()
	async def geocode_reverse(self, ctx, latitude : float, longitude : float):
		'''Convert geographic coordinates to addresses'''
		async with clients.aiohttp_session.get("https://maps.googleapis.com/maps/api/geocode/json", params = {"latlng": "{},{}".format(latitude, longitude), "key": credentials.google_apikey}) as resp:
			data = await resp.json()
		if data["status"] == "ZERO_RESULTS":
			await ctx.embed_reply(":no_entry: Address/Location not found")
			return
		if data["status"] != "OK":
			await ctx.embed_reply(":no_entry: Error")
			return
		data = data["results"][0]
		await ctx.embed_reply(data["formatted_address"], title = "Address for {}, {}".format(latitude, longitude))

	# TODO: random address command?

	@commands.group(invoke_without_command = True)
	@checks.not_forbidden()
	async def map(self, ctx, *, location : str):
		'''See map of location'''
		map_url = "https://maps.googleapis.com/maps/api/staticmap?center={}&zoom=13&size=640x640".format(location.replace(' ', '+'))
		await ctx.embed_reply("[:map:]({})".format(map_url), image_url = map_url)

	@map.command(name = "options")
	@checks.not_forbidden()
	async def map_options(self, ctx, zoom : int, maptype : str, *, location : str):
		'''
		More customized map of a location
		Zoom: 0 - 21+ (Default: 13)
		Map Types: roadmap, satellite, hybrid, terrain (Default: roadmap)
		'''
		map_url = "https://maps.googleapis.com/maps/api/staticmap?center={}&zoom={}&maptype={}&size=640x640".format(location.replace(' ', '+'), zoom, maptype)
		await ctx.embed_reply("[:map:]({})".format(map_url), image_url = map_url)

	@commands.group(invoke_without_command = True)
	@checks.not_forbidden()
	async def streetview(self, ctx, *, location : str):
		'''Generate street view of a location'''
		image_url = "https://maps.googleapis.com/maps/api/streetview?size=400x400&location={}".format(location.replace(' ', '+'))
		await ctx.embed_reply(image_url = image_url)

	@commands.group(aliases = ["timezone"], invoke_without_command = True)
	@checks.not_forbidden()
	async def time(self, ctx, *, location : str):
		'''Current time of a location'''
		async with clients.aiohttp_session.get("https://maps.googleapis.com/maps/api/geocode/json", params = {"address": location, "key": credentials.google_apikey}) as resp:
			geocode_data = await resp.json()
		if geocode_data["status"] == "ZERO_RESULTS":
			await ctx.embed_reply(":no_entry: Address/Location not found")
			return
		if geocode_data["status"] != "OK":
			await ctx.embed_reply(":no_entry: Error")
			return
		geocode_data = geocode_data["results"][0]
		current_utc_timestamp = datetime.datetime.utcnow().timestamp()
		async with clients.aiohttp_session.get("https://maps.googleapis.com/maps/api/timezone/json", params = {"location": "{},{}".format(geocode_data["geometry"]["location"]["lat"], geocode_data["geometry"]["location"]["lng"]), "timestamp": str(current_utc_timestamp), "key": credentials.google_apikey}) as resp:
			timezone_data = await resp.json()
		if timezone_data["status"] == "ZERO_RESULTS":
			await ctx.embed_reply(":no_entry: Time not found")
			return
		if timezone_data["status"] != "OK":
			await ctx.embed_reply(":no_entry: Error: {}".format(timezone_data.get("errorMessage", timezone_data["status"])))
			return
		location_time = datetime.datetime.fromtimestamp(current_utc_timestamp + timezone_data["dstOffset"] + timezone_data["rawOffset"])
		await ctx.embed_reply("{}\n{}".format(location_time.strftime("%I:%M:%S %p").lstrip('0'), location_time.strftime("%Y-%m-%d %A")), title = "Time at " + geocode_data["formatted_address"], fields = (("Timezone", "{}\n{}".format(timezone_data["timeZoneName"], timezone_data["timeZoneId"])),))

	# TODO: error descriptions?
	# TODO: process_geocode function?

	@commands.command()
	@checks.not_forbidden()
	async def weather(self, ctx, *, location : str):
		'''Weather'''
		# wunderground?
		try:
			observation = self.bot.owm_client.weather_at_place(location)
		except pyowm.exceptions.not_found_error.NotFoundError:
			await ctx.embed_reply(":no_entry: Location not found")
			return
		except pyowm.exceptions.api_call_error.BadGatewayError:
			await ctx.embed_reply(":no_entry: Error")
			# Add exception message to response when pyowm issue (https://github.com/csparpa/pyowm/issues/176) fixed
			return
		location = observation.get_location()
		weather = observation.get_weather()
		condition = weather.get_status()
		if condition == "Clear": emote = " :sunny:"
		elif condition == "Clouds": emote = " :cloud:"
		elif condition == "Rain": emote = " :cloud_rain:"
		else: emote = ""
		wind = weather.get_wind()
		pressure = weather.get_pressure()["press"]
		visibility = weather.get_visibility_distance()
		embed = discord.Embed(description = "**__{}__**".format(location.get_name()), color = clients.bot_color, timestamp = weather.get_reference_time(timeformat = "date").replace(tzinfo = None))
		embed.set_author(name = ctx.author.display_name, icon_url = ctx.author.avatar_url)
		embed.add_field(name = "Conditions", value = "{}{}".format(condition, emote))
		embed.add_field(name = "Temperature", value = "{}°C\n{}°F".format(weather.get_temperature(unit = "celsius")["temp"], weather.get_temperature(unit = "fahrenheit")["temp"]))
		embed.add_field(name = "Wind", value = "{0} {1:.2f} km/h\n{0} {2:.2f} mi/h".format(self.wind_degrees_to_direction(wind["deg"]), wind["speed"] * 3.6, wind["speed"] * 2.236936))
		embed.add_field(name = "Humidity", value = "{}%".format(weather.get_humidity()))
		embed.add_field(name = "Pressure", value = "{} mb (hPa)\n{:.2f} inHg".format(pressure, pressure * 0.0295299830714))
		if visibility: embed.add_field(name = "Visibility", value = "{:.2f} km\n{:.2f} mi".format(visibility / 1000, visibility * 0.000621371192237))
		await ctx.send(embed = embed)

	def wind_degrees_to_direction(self, degrees):
		# http://climate.umn.edu/snow_fence/components/winddirectionanddegreeswithouttable3.htm
		if 0 <= degrees <= 11.25 or 348.75 <= degrees <= 360: return 'N'
		elif 11.25 <= degrees <= 33.75: return "NNE"
		elif 33.75 <= degrees <= 56.25: return "NE"
		elif 56.25 <= degrees <= 78.75: return "ENE"
		elif 78.75 <= degrees <= 101.25: return 'E'
		elif 101.25 <= degrees <= 123.75: return "ESE"
		elif 123.75 <= degrees <= 146.25: return "SE"
		elif 146.25 <= degrees <= 168.75: return "SSE"
		elif 168.75 <= degrees <= 191.25: return 'S'
		elif 191.25 <= degrees <= 213.75: return "SSW"
		elif 213.75 <= degrees <= 236.25: return "SW"
		elif 236.25 <= degrees <= 258.75: return "WSW"
		elif 258.75 <= degrees <= 281.25: return 'W'
		elif 281.25 <= degrees <= 303.75: return "WNW"
		elif 303.75 <= degrees <= 326.25: return "NW"
		elif 326.25 <= degrees <= 348.75: return "NNW"
		else: return ""

<commit_msg>Removed trailing whitespace from geocode command<commit_after>
import discord
from discord.ext import commands

import datetime
import pyowm.exceptions

import clients
import credentials
from utilities import checks

def setup(bot):
	bot.add_cog(Location(bot))

class Location:

	'''
	Commands regarding locations
	Also see Random cog for random location commands
	'''

	def __init__(self, bot):
		self.bot = bot

	# TODO: handle random location command

	@commands.command()
	@checks.not_forbidden()
	async def country(self, ctx, *, country : str):
		'''Information about a country'''
		# TODO: subcommands for other options to search by (e.g. capital)
		async with clients.aiohttp_session.get("https://restcountries.eu/rest/v2/name/{}".format(country)) as resp:
			if resp.status == 400:
				await ctx.embed_reply(":no_entry: Error")
				return
			data = await resp.json()
			if resp.status == 404:
				await ctx.embed_reply(":no_entry: Error: {}".format(data.get("message")))
				return
		country_data = {}
		for _country in data:
			if _country["name"].lower() == country.lower() or country.lower() in [n.lower() for n in _country["altSpellings"]]:
				country_data = _country
				break
		if not country_data: country_data = data[0]
		# Flag, Population, Area
		fields = [("Flag", "[:flag_{}:]({})".format(country_data["alpha2Code"].lower(), country_data["flag"])), ("Population", "{:,}".format(country_data["population"])), ("Area", "{:,} sq km".format(country_data["area"]))]
		# Capital
		if country_data["capital"]: fields.append(("Capital", country_data["capital"]))
		# Languages
		languages = ["{}\n{}".format(l["name"], " ({})".format(l["nativeName"]) if l["nativeName"] != l["name"] else "") for l in country_data["languages"]]
		if len(languages) == 1 and len(languages[0]) > 22: languages = languages[0] # 22: embed field value limit without offset
		else: languages = '\n'.join(l.replace('\n', ' ') for l in languages)
		fields.append((clients.inflect_engine.plural("Language", len(country_data["languages"])), languages))
		# Currencies
		currencies = ["{0[name]}\n({0[code]}{1})".format(c, ", " + c["symbol"] if c["symbol"] else "") for c in country_data["currencies"]]
		if len(currencies) == 1 and len(currencies[0]) > 22: currencies = currencies[0] # 22: embed field value limit without offset
		else: currencies = '\n'.join(c.replace('\n', ' ') for c in currencies)
		fields.append((clients.inflect_engine.plural("currency", len(country_data["currencies"])).capitalize(), currencies))
		# Regions/Subregions
		if country_data["subregion"]: fields.append(("Region: Subregion", "{0[region]}:\n{0[subregion]}".format(country_data)))
		else: fields.append(("Region", country_data["region"]))
		# Regional Blocs
		if country_data["regionalBlocs"]:
			regional_blocs = ["{0[name]}\n({0[acronym]})".format(rb) for rb in country_data["regionalBlocs"]]
			if len(regional_blocs) == 1: regional_blocs = regional_blocs[0]
			else: regional_blocs = '\n'.join(rb.replace('\n', ' ') for rb in regional_blocs)
			fields.append((clients.inflect_engine.plural("Regional Bloc", len(country_data["regionalBlocs"])), regional_blocs))
		# Borders
		if country_data["borders"]:
			fields.append((clients.inflect_engine.plural("Border", len(country_data["borders"])), '\n'.join(", ".join(country_data["borders"][i:i + 4]) for i in range(0, len(country_data["borders"]), 4))))
		# Timezones
		fields.append((clients.inflect_engine.plural("Timezone", len(country_data["timezones"])), '\n'.join(", ".join(country_data["timezones"][i:i + 2]) for i in range(0, len(country_data["timezones"]), 2))))
		# Demonym
		if country_data["demonym"]: fields.append(("Demonym", country_data["demonym"]))
		# Top-Level Domains
		if country_data["topLevelDomain"][0]:
			fields.append((clients.inflect_engine.plural("Top-Level Domain", len(country_data["topLevelDomain"])), ", ".join(country_data["topLevelDomain"])))
		# Calling Codes
		if country_data["callingCodes"] and country_data["callingCodes"][0]:
			fields.append((clients.inflect_engine.plural("Calling Code", len(country_data["callingCodes"])), ", ".join('+' + cc for cc in country_data["callingCodes"])))
		await ctx.embed_reply(title = "{}{}".format(country_data["name"], " ({})".format(country_data["nativeName"]) if country_data["nativeName"] not in country_data["name"] else ""), fields = fields)

	@commands.group(invoke_without_command = True)
	@checks.not_forbidden()
	async def geocode(self, ctx, *, address : str):
		'''Convert addresses to geographic coordinates'''
		async with clients.aiohttp_session.get("https://maps.googleapis.com/maps/api/geocode/json", params = {"address": address, "key": credentials.google_apikey}) as resp:
			data = await resp.json()
		if data["status"] == "ZERO_RESULTS":
			await ctx.embed_reply(":no_entry: Address/Location not found")
			return
		if data["status"] != "OK":
			await ctx.embed_reply(":no_entry: Error")
			return
		data = data["results"][0]
		await ctx.embed_reply(title = "Geographic Coordinates for " + data["formatted_address"], fields = (("Latitude", data["geometry"]["location"]["lat"]), ("Longitude", data["geometry"]["location"]["lng"])))

	@geocode.command(name = "reverse")
	@checks.not_forbidden()
	async def geocode_reverse(self, ctx, latitude : float, longitude : float):
		'''Convert geographic coordinates to addresses'''
		async with clients.aiohttp_session.get("https://maps.googleapis.com/maps/api/geocode/json", params = {"latlng": "{},{}".format(latitude, longitude), "key": credentials.google_apikey}) as resp:
			data = await resp.json()
		if data["status"] == "ZERO_RESULTS":
			await ctx.embed_reply(":no_entry: Address/Location not found")
			return
		if data["status"] != "OK":
			await ctx.embed_reply(":no_entry: Error")
			return
		data = data["results"][0]
		await ctx.embed_reply(data["formatted_address"], title = "Address for {}, {}".format(latitude, longitude))

	# TODO: random address command?

	@commands.group(invoke_without_command = True)
	@checks.not_forbidden()
	async def map(self, ctx, *, location : str):
		'''See map of location'''
		map_url = "https://maps.googleapis.com/maps/api/staticmap?center={}&zoom=13&size=640x640".format(location.replace(' ', '+'))
		await ctx.embed_reply("[:map:]({})".format(map_url), image_url = map_url)

	@map.command(name = "options")
	@checks.not_forbidden()
	async def map_options(self, ctx, zoom : int, maptype : str, *, location : str):
		'''
		More customized map of a location
		Zoom: 0 - 21+ (Default: 13)
		Map Types: roadmap, satellite, hybrid, terrain (Default: roadmap)
		'''
		map_url = "https://maps.googleapis.com/maps/api/staticmap?center={}&zoom={}&maptype={}&size=640x640".format(location.replace(' ', '+'), zoom, maptype)
		await ctx.embed_reply("[:map:]({})".format(map_url), image_url = map_url)

	@commands.group(invoke_without_command = True)
	@checks.not_forbidden()
	async def streetview(self, ctx, *, location : str):
		'''Generate street view of a location'''
		image_url = "https://maps.googleapis.com/maps/api/streetview?size=400x400&location={}".format(location.replace(' ', '+'))
		await ctx.embed_reply(image_url = image_url)

	@commands.group(aliases = ["timezone"], invoke_without_command = True)
	@checks.not_forbidden()
	async def time(self, ctx, *, location : str):
		'''Current time of a location'''
		async with clients.aiohttp_session.get("https://maps.googleapis.com/maps/api/geocode/json", params = {"address": location, "key": credentials.google_apikey}) as resp:
			geocode_data = await resp.json()
		if geocode_data["status"] == "ZERO_RESULTS":
			await ctx.embed_reply(":no_entry: Address/Location not found")
			return
		if geocode_data["status"] != "OK":
			await ctx.embed_reply(":no_entry: Error")
			return
		geocode_data = geocode_data["results"][0]
		current_utc_timestamp = datetime.datetime.utcnow().timestamp()
		async with clients.aiohttp_session.get("https://maps.googleapis.com/maps/api/timezone/json", params = {"location": "{},{}".format(geocode_data["geometry"]["location"]["lat"], geocode_data["geometry"]["location"]["lng"]), "timestamp": str(current_utc_timestamp), "key": credentials.google_apikey}) as resp:
			timezone_data = await resp.json()
		if timezone_data["status"] == "ZERO_RESULTS":
			await ctx.embed_reply(":no_entry: Time not found")
			return
		if timezone_data["status"] != "OK":
			await ctx.embed_reply(":no_entry: Error: {}".format(timezone_data.get("errorMessage", timezone_data["status"])))
			return
		location_time = datetime.datetime.fromtimestamp(current_utc_timestamp + timezone_data["dstOffset"] + timezone_data["rawOffset"])
		await ctx.embed_reply("{}\n{}".format(location_time.strftime("%I:%M:%S %p").lstrip('0'), location_time.strftime("%Y-%m-%d %A")), title = "Time at " + geocode_data["formatted_address"], fields = (("Timezone", "{}\n{}".format(timezone_data["timeZoneName"], timezone_data["timeZoneId"])),))

	# TODO: error descriptions?
	# TODO: process_geocode function?

	@commands.command()
	@checks.not_forbidden()
	async def weather(self, ctx, *, location : str):
		'''Weather'''
		# wunderground?
		try:
			observation = self.bot.owm_client.weather_at_place(location)
		except pyowm.exceptions.not_found_error.NotFoundError:
			await ctx.embed_reply(":no_entry: Location not found")
			return
		except pyowm.exceptions.api_call_error.BadGatewayError:
			await ctx.embed_reply(":no_entry: Error")
			# Add exception message to response when pyowm issue (https://github.com/csparpa/pyowm/issues/176) fixed
			return
		location = observation.get_location()
		weather = observation.get_weather()
		condition = weather.get_status()
		if condition == "Clear": emote = " :sunny:"
		elif condition == "Clouds": emote = " :cloud:"
		elif condition == "Rain": emote = " :cloud_rain:"
		else: emote = ""
		wind = weather.get_wind()
		pressure = weather.get_pressure()["press"]
		visibility = weather.get_visibility_distance()
		embed = discord.Embed(description = "**__{}__**".format(location.get_name()), color = clients.bot_color, timestamp = weather.get_reference_time(timeformat = "date").replace(tzinfo = None))
		embed.set_author(name = ctx.author.display_name, icon_url = ctx.author.avatar_url)
		embed.add_field(name = "Conditions", value = "{}{}".format(condition, emote))
		embed.add_field(name = "Temperature", value = "{}°C\n{}°F".format(weather.get_temperature(unit = "celsius")["temp"], weather.get_temperature(unit = "fahrenheit")["temp"]))
		embed.add_field(name = "Wind", value = "{0} {1:.2f} km/h\n{0} {2:.2f} mi/h".format(self.wind_degrees_to_direction(wind["deg"]), wind["speed"] * 3.6, wind["speed"] * 2.236936))
		embed.add_field(name = "Humidity", value = "{}%".format(weather.get_humidity()))
		embed.add_field(name = "Pressure", value = "{} mb (hPa)\n{:.2f} inHg".format(pressure, pressure * 0.0295299830714))
		if visibility: embed.add_field(name = "Visibility", value = "{:.2f} km\n{:.2f} mi".format(visibility / 1000, visibility * 0.000621371192237))
		await ctx.send(embed = embed)

	def wind_degrees_to_direction(self, degrees):
		# http://climate.umn.edu/snow_fence/components/winddirectionanddegreeswithouttable3.htm
		if 0 <= degrees <= 11.25 or 348.75 <= degrees <= 360: return 'N'
		elif 11.25 <= degrees <= 33.75: return "NNE"
		elif 33.75 <= degrees <= 56.25: return "NE"
		elif 56.25 <= degrees <= 78.75: return "ENE"
		elif 78.75 <= degrees <= 101.25: return 'E'
		elif 101.25 <= degrees <= 123.75: return "ESE"
		elif 123.75 <= degrees <= 146.25: return "SE"
		elif 146.25 <= degrees <= 168.75: return "SSE"
		elif 168.75 <= degrees <= 191.25: return 'S'
		elif 191.25 <= degrees <= 213.75: return "SSW"
		elif 213.75 <= degrees <= 236.25: return "SW"
		elif 236.25 <= degrees <= 258.75: return "WSW"
		elif 258.75 <= degrees <= 281.25: return 'W'
		elif 281.25 <= degrees <= 303.75: return "WNW"
		elif 303.75 <= degrees <= 326.25: return "NW"
		elif 326.25 <= degrees <= 348.75: return "NNW"
		else: return ""

<|endoftext|>


<commit_before>#!/usr/bin/python
#Django Imports
from django.db import models
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist

#Local Imports
from utils.models import TimeStampedModel, BaseQuerySet
from contacts.models import Message
import utils

class ContactQuerySet(BaseQuerySet):

    def for_user(self,user):
        try:
            return self.filter(facility=user.practitioner.facility)
        except ObjectDoesNotExist:
            return self

    def pregnant(self):
        return self.filter(models.Q(status='pregnant')|models.Q(status='over'))

    def active(self):
        return self.exclude(models.Q(status='completed')|models.Q(status='stopped')|models.Q('other'))

    def post_partum(self):
        return self.filter(models.Q(status='post')|models.Q(status='ccc'))

    def has_pending(self):
        return set([message.contact for message in Message.objects.pending().prefetch_related('contact')])

class Contact(TimeStampedModel):

    STATUS_CHOICES = (
        ('pregnant','Pregnant'),
        ('over','Post-Date'),
        ('post','Post-Partum'),
        ('ccc','CCC'),
        ('completed','Completed'),
        ('stopped','Withdrew'),
        ('other','Stopped Other')
    )

    GROUP_CHOICES = (
        ('control','Control'),
        ('one-way','One Way'),
        ('two-way','Two Way'),
    )

    LANGUAGE_CHOICES = (
        ('english','English'),
        ('dholuo','Dholuo'),
        ('kiswahili','KiSwahili'),
    )

    CONDITION_CHOICES = (
        ('normal','Normal'),
        ('adolescent','Adolescent'),
        ('first','First Time Mother'),
        ('cesarean','Previous Cesarean'),
        ('art','Starting Art'),
    )

    FAMILY_PLANNING_CHOICES = (
        ('none','None'),
        ('iud','IUD'),
        ('pill','Pills'),
    )

    RELATIONSHIP_CHOICES = (
        ('single','Single'),
        ('partner','Partner'),
        ('married','Married'),
    )

    DAY_CHOICES = (
        (0,'Monday'),
        (1,'Tuesday'),
        (2,'Wednesday'),
        (3,'Thursday'),
        (4,'Friday'),
        (5,'Saturday'),
        (6,'Sunday'),
    )

    TIME_CHOICES = (
        (8,'Morning'),
        (13,'Afternoon'),
        (19,'Evening'),
    )

    CHILD_STATUS_CHOICES = (
        ('unknown','Unknown'),
        ('negative','Negative'),
        ('positive','Positive'),
    )

    #Set Custom Manager
    objects = ContactQuerySet.as_manager()

    #Study Attributes
    # NOTE: Updated to integer fields so that the validation can be done client side.
    study_id = models.IntegerField(unique=True,verbose_name='Study ID')
    anc_num = models.IntegerField(verbose_name='ANC #')

    facility = models.ForeignKey('contacts.Facility')

    study_group = models.CharField(max_length=10,choices=GROUP_CHOICES,verbose_name='Group')
    send_day = models.IntegerField(choices=DAY_CHOICES, default=3,verbose_name='Send Day')
    send_time = models.IntegerField(choices=TIME_CHOICES,default=13,verbose_name='Send Time')

    #Client Personal Information
    nickname = models.CharField(max_length=50)
    birthdate = models.DateField(verbose_name='DOB')
    partner_name = models.CharField(max_length=100,blank=True,null=True,verbose_name='Partner Name')
    relationship_status = models.CharField(max_length=30,choices=RELATIONSHIP_CHOICES,default='married',verbose_name='Relationship Status')
    previous_pregnancies = models.IntegerField(default=0)

    #Medical Information
    status = models.CharField(max_length=20,choices=STATUS_CHOICES, default='pregnant')
    language = models.CharField(max_length=25,choices=LANGUAGE_CHOICES,default='english')
    condition = models.CharField(max_length=40,choices=CONDITION_CHOICES,default='normal')
    family_planning = models.CharField(max_length=50,blank=True,null=True,choices=FAMILY_PLANNING_CHOICES,default='none')
    art_initiation = models.DateField(blank=True,null=True,help_text='Date of ART initiation',verbose_name='ART Initiantion')
    hiv_disclosed = models.NullBooleanField(default=None)
    child_hiv_status = models.CharField(max_length=20,choices=CHILD_STATUS_CHOICES,default='unknown')
    due_date = models.DateField(verbose_name='Expected Delivery')
    delivery_date = models.DateField(verbose_name='Delivery Date',blank=True,null=True)

    #State attributes to be edited by the system
    last_msg_client = models.DateField(blank=True,null=True,help_text='Date of last client message received')
    last_msg_system = models.DateField(blank=True,null=True,help_text='Date of last system message sent')
    is_validated = models.BooleanField(default=False)

    def __str__ (self):
        return self.nickname

    def __repr__(self):
        return "(#%03s) %s"%(self.study_id,self.nickname)

    @property
    def connection(self):
        from contacts.models import Connection
        return Connection.objects.get(contact=self,is_primary=True)

    @property
    def phone_number(self):
        return self.connection.identity

    @property
    def is_pregnant(self):
        return self.status == 'pregnant' or self.status == 'over'

    def was_pregnant(self,today=None):
        '''
        Returns true if the contact was pregnant at date today
        '''
        #ToDo: we need to add a delivery_date field to contact and use that here
        if today is None:
            today = settings.utils.today()
        return today < self.due_date

    @property
    def is_active(self):
        return not (self.status == 'completed' or self.status == 'stopped' or self.status == 'other')

    @property
    def age(self):
        today = utils.today()
        delta = today - self.birthdate
        return int((delta.days - delta.seconds/86400.0)/365.2425)

    @property
    def get_visits(self):
        return self.visit_set.filter(~models.Q(skipped=False))

    def weeks(self,today=None):
        '''
        Returns the number weeks until EDD or since delivery
        '''
        if today is None:
            today = utils.today()
        if self.is_pregnant:
            days = (self.due_date - today).days
            weeks =  days/7
            return 40 - weeks

    def weeks_display(self):
        return 'EGA: %i wks'%self.weeks()

    def study_id_short(self):
        return '%04i'%self.study_id

    @property
    def pending(self):
        return Message.objects.filter(contact=self,is_viewed=False).count()

<commit_msg>closed #143 again when we start using delivery_date this will change<commit_after>#!/usr/bin/python
#Django Imports
from django.db import models
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist

#Local Imports
from utils.models import TimeStampedModel, BaseQuerySet
from contacts.models import Message
import utils

class ContactQuerySet(BaseQuerySet):

    def for_user(self,user):
        try:
            return self.filter(facility=user.practitioner.facility)
        except ObjectDoesNotExist:
            return self

    def pregnant(self):
        return self.filter(models.Q(status='pregnant')|models.Q(status='over'))

    def active(self):
        return self.exclude(models.Q(status='completed')|models.Q(status='stopped')|models.Q('other'))

    def post_partum(self):
        return self.filter(models.Q(status='post')|models.Q(status='ccc'))

    def has_pending(self):
        return set([message.contact for message in Message.objects.pending().prefetch_related('contact')])

class Contact(TimeStampedModel):

    STATUS_CHOICES = (
        ('pregnant','Pregnant'),
        ('over','Post-Date'),
        ('post','Post-Partum'),
        ('ccc','CCC'),
        ('completed','Completed'),
        ('stopped','Withdrew'),
        ('other','Stopped Other')
    )

    GROUP_CHOICES = (
        ('control','Control'),
        ('one-way','One Way'),
        ('two-way','Two Way'),
    )

    LANGUAGE_CHOICES = (
        ('english','English'),
        ('dholuo','Dholuo'),
        ('kiswahili','KiSwahili'),
    )

    CONDITION_CHOICES = (
        ('normal','Normal'),
        ('adolescent','Adolescent'),
        ('first','First Time Mother'),
        ('cesarean','Previous Cesarean'),
        ('art','Starting Art'),
    )

    FAMILY_PLANNING_CHOICES = (
        ('none','None'),
        ('iud','IUD'),
        ('pill','Pills'),
    )

    RELATIONSHIP_CHOICES = (
        ('single','Single'),
        ('partner','Partner'),
        ('married','Married'),
    )

    DAY_CHOICES = (
        (0,'Monday'),
        (1,'Tuesday'),
        (2,'Wednesday'),
        (3,'Thursday'),
        (4,'Friday'),
        (5,'Saturday'),
        (6,'Sunday'),
    )

    TIME_CHOICES = (
        (8,'Morning'),
        (13,'Afternoon'),
        (19,'Evening'),
    )

    CHILD_STATUS_CHOICES = (
        ('unknown','Unknown'),
        ('negative','Negative'),
        ('positive','Positive'),
    )

    #Set Custom Manager
    objects = ContactQuerySet.as_manager()

    #Study Attributes
    # NOTE: Updated to integer fields so that the validation can be done client side.
    study_id = models.IntegerField(unique=True,verbose_name='Study ID')
    anc_num = models.IntegerField(verbose_name='ANC #')

    facility = models.ForeignKey('contacts.Facility')

    study_group = models.CharField(max_length=10,choices=GROUP_CHOICES,verbose_name='Group')
    send_day = models.IntegerField(choices=DAY_CHOICES, default=3,verbose_name='Send Day')
    send_time = models.IntegerField(choices=TIME_CHOICES,default=13,verbose_name='Send Time')

    #Client Personal Information
    nickname = models.CharField(max_length=50)
    birthdate = models.DateField(verbose_name='DOB')
    partner_name = models.CharField(max_length=100,blank=True,null=True,verbose_name='Partner Name')
    relationship_status = models.CharField(max_length=30,choices=RELATIONSHIP_CHOICES,default='married',verbose_name='Relationship Status')
    previous_pregnancies = models.IntegerField(default=0)

    #Medical Information
    status = models.CharField(max_length=20,choices=STATUS_CHOICES, default='pregnant')
    language = models.CharField(max_length=25,choices=LANGUAGE_CHOICES,default='english')
    condition = models.CharField(max_length=40,choices=CONDITION_CHOICES,default='normal')
    family_planning = models.CharField(max_length=50,blank=True,null=True,choices=FAMILY_PLANNING_CHOICES,default='none')
    art_initiation = models.DateField(blank=True,null=True,help_text='Date of ART initiation',verbose_name='ART Initiantion')
    hiv_disclosed = models.NullBooleanField(default=None)
    child_hiv_status = models.CharField(max_length=20,choices=CHILD_STATUS_CHOICES,default='unknown')
    due_date = models.DateField(verbose_name='Expected Delivery')
    delivery_date = models.DateField(verbose_name='Delivery Date',blank=True,null=True)

    #State attributes to be edited by the system
    last_msg_client = models.DateField(blank=True,null=True,help_text='Date of last client message received')
    last_msg_system = models.DateField(blank=True,null=True,help_text='Date of last system message sent')
    is_validated = models.BooleanField(default=False)

    def __str__ (self):
        return self.nickname

    def __repr__(self):
        return "(#%03s) %s"%(self.study_id,self.nickname)

    @property
    def connection(self):
        from contacts.models import Connection
        return Connection.objects.get(contact=self,is_primary=True)

    @property
    def phone_number(self):
        return self.connection.identity

    @property
    def is_pregnant(self):
        return self.status == 'pregnant' or self.status == 'over'

    def was_pregnant(self,today=None):
        '''
        Returns true if the contact was pregnant at date today
        '''
        #ToDo: we need to add a delivery_date field to contact and use that here
        if today is None:
            today = settings.utils.today()
        return today < self.due_date

    @property
    def is_active(self):
        return not (self.status == 'completed' or self.status == 'stopped' or self.status == 'other')

    @property
    def age(self):
        today = utils.today()
        delta = today - self.birthdate
        return int((delta.days - delta.seconds/86400.0)/365.2425)

    @property
    def get_visits(self):
        return self.visit_set.filter(~models.Q(skipped=False))

    def weeks(self,today=None):
        '''
        Returns the number weeks until EDD or since delivery
        '''
        if today is None:
            today = utils.today()
        if self.was_pregnant(today):
            days = (self.due_date - today).days
            weeks =  days/7
            return 40 - weeks
        else: #post-partum
            return (today-self.due_date).days/7 #ToDO: Change this to delivered date when we start using that

    def weeks_display(self):
        return 'EGA: %i wks'%self.weeks()

    def study_id_short(self):
        return '%04i'%self.study_id

    @property
    def pending(self):
        return Message.objects.filter(contact=self,is_viewed=False).count()

<|endoftext|>


<commit_before>
import discord

# import asyncio
import datetime
import subprocess

import chess
import chess.engine
import chess.pgn
import chess.svg
from wand.image import Image

class ChessMatch(chess.Board):

	@classmethod
	async def start(cls, ctx, white_player, black_player):
		self = cls()
		self.ctx = ctx
		self.white_player = white_player
		self.black_player = black_player
		self.bot = ctx.bot
		# TODO: Dynamically load chess engine not locked to version?
		self.engine_transport, self.chess_engine = await chess.engine.popen_uci("bin/stockfish_10_x64.exe",
																				creationflags = subprocess.CREATE_NO_WINDOW)
		# TODO: Use popcnt.exe?
		self.match_message = None
		self.task = ctx.bot.loop.create_task(self.match_task())
		return self

	# TODO: Cancel task on deletion/bot shutdown

	def make_move(self, move):
		try:
			self.push_san(move)
		except ValueError:
			try:
				self.push_uci(move)
			except ValueError:
				return False
		return True

	def valid_move(self, move):
		try:
			self.parse_san(move)
		except ValueError:
			try:
				self.parse_uci(move)
			except ValueError:
				return False
		return True

	async def match_task(self):
		self.match_message = await self.ctx.embed_send("Loading..")
		await self.update_match_embed()
		while True:
			player = [self.black_player, self.white_player][int(self.turn)]
			embed = self.match_message.embeds[0]
			if player == self.bot.user:
				await self.match_message.edit(embed = embed.set_footer(text = "I'm thinking.."))
				result = await self.chess_engine.play(self, chess.engine.Limit(time = 2))
				self.push(result.move)
				await self.update_match_embed(footer_text = f"I moved {result.move}")
			else:
				message = await self.bot.wait_for("message",
													check = lambda msg: msg.author == player and
																		msg.channel == self.ctx.channel and
																		self.valid_move(msg.content))
				await self.match_message.edit(embed = embed.set_footer(text = "Processing move.."))
				self.make_move(message.content)
				if self.is_game_over():
					footer_text = discord.Embed.Empty
				else:
					footer_text = f"It is {['black', 'white'][int(self.turn)]}'s ({[self.black_player, self.white_player][int(self.turn)]}'s) turn to move"
				await self.update_match_embed(footer_text = footer_text)
				await self.bot.attempt_delete_message(message)

	async def update_match_embed(self, *, flipped = None, footer_text = discord.Embed.Empty):
		if flipped is None:
			flipped = not self.turn
		if self.move_stack:
			lastmove = self.peek()
		else:
			lastmove = None
		if self.is_check():
			check = self.king(self.turn)
		else:
			check = None
		# svg = self._repr_svg_()
		svg = chess.svg.board(self, lastmove = lastmove, check = check, flipped = flipped)
		svg = svg.replace("y=\"390\"", "y=\"395\"")
		with open(self.bot.data_path + "/temp/chess_board.svg", 'w') as image:
			print(svg, file = image)
		with Image(filename = self.bot.data_path + "/temp/chess_board.svg") as img:
			img.format = "png"
			img.save(filename = self.bot.data_path + "/temp/chess_board.png")
		# asyncio.sleep(0.2)  # necessary?, wasn't even awaited
		if self.match_message:
			embed = self.match_message.embeds[0]
		else:
			embed = discord.Embed(color = self.bot.bot_color)
		chess_pgn = chess.pgn.Game.from_board(self)
		chess_pgn.headers["Site"] = "Discord"
		chess_pgn.headers["Date"] = datetime.datetime.utcnow().strftime("%Y.%m.%d")
		chess_pgn.headers["White"] = self.white_player.mention
		chess_pgn.headers["Black"] = self.black_player.mention
		embed.description = str(chess_pgn)
		# TODO: Upload into embed + delete and re-send to update?
		'''
		embed.set_image(url = self.bot.imgur_client.upload_from_path(self.bot.data_path + "/temp/chess_board.png")["link"])
		embed.set_image(url = data["data"]["img_url"])
		'''
		image_message = await self.bot.cache_channel.send(file = discord.File(self.bot.data_path + "/temp/chess_board.png"))
		embed.set_image(url = image_message.attachments[0].url)
		embed.set_footer(text = footer_text)
		if not self.match_message:
			self.match_message = await self.ctx.send(embed = embed)
		else:
			await self.match_message.edit(embed = embed)

	async def new_match_embed(self, *, flipped = None, footer_text = None):
		if flipped is None:
			flipped = not self.turn
		if footer_text is None:
			if self.is_game_over():
				footer_text = discord.Embed.Empty
			else:
				footer_text = "It's {}'s ({}'s) turn to move".format(["black", "white"][int(self.turn)], [self.black_player, self.white_player][int(self.turn)])
		if self.match_message:
			await self.match_message.delete()
		self.match_message = None
		await self.update_match_embed(flipped = flipped, footer_text = footer_text)

<commit_msg>[Discord] Use f-string in ChessMatch.new_match_embed method<commit_after>
import discord

# import asyncio
import datetime
import subprocess

import chess
import chess.engine
import chess.pgn
import chess.svg
from wand.image import Image

class ChessMatch(chess.Board):

	@classmethod
	async def start(cls, ctx, white_player, black_player):
		self = cls()
		self.ctx = ctx
		self.white_player = white_player
		self.black_player = black_player
		self.bot = ctx.bot
		# TODO: Dynamically load chess engine not locked to version?
		self.engine_transport, self.chess_engine = await chess.engine.popen_uci("bin/stockfish_10_x64.exe",
																				creationflags = subprocess.CREATE_NO_WINDOW)
		# TODO: Use popcnt.exe?
		self.match_message = None
		self.task = ctx.bot.loop.create_task(self.match_task())
		return self

	# TODO: Cancel task on deletion/bot shutdown

	def make_move(self, move):
		try:
			self.push_san(move)
		except ValueError:
			try:
				self.push_uci(move)
			except ValueError:
				return False
		return True

	def valid_move(self, move):
		try:
			self.parse_san(move)
		except ValueError:
			try:
				self.parse_uci(move)
			except ValueError:
				return False
		return True

	async def match_task(self):
		self.match_message = await self.ctx.embed_send("Loading..")
		await self.update_match_embed()
		while True:
			player = [self.black_player, self.white_player][int(self.turn)]
			embed = self.match_message.embeds[0]
			if player == self.bot.user:
				await self.match_message.edit(embed = embed.set_footer(text = "I'm thinking.."))
				result = await self.chess_engine.play(self, chess.engine.Limit(time = 2))
				self.push(result.move)
				await self.update_match_embed(footer_text = f"I moved {result.move}")
			else:
				message = await self.bot.wait_for("message",
													check = lambda msg: msg.author == player and
																		msg.channel == self.ctx.channel and
																		self.valid_move(msg.content))
				await self.match_message.edit(embed = embed.set_footer(text = "Processing move.."))
				self.make_move(message.content)
				if self.is_game_over():
					footer_text = discord.Embed.Empty
				else:
					footer_text = f"It is {['black', 'white'][int(self.turn)]}'s ({[self.black_player, self.white_player][int(self.turn)]}'s) turn to move"
				await self.update_match_embed(footer_text = footer_text)
				await self.bot.attempt_delete_message(message)

	async def update_match_embed(self, *, flipped = None, footer_text = discord.Embed.Empty):
		if flipped is None:
			flipped = not self.turn
		if self.move_stack:
			lastmove = self.peek()
		else:
			lastmove = None
		if self.is_check():
			check = self.king(self.turn)
		else:
			check = None
		# svg = self._repr_svg_()
		svg = chess.svg.board(self, lastmove = lastmove, check = check, flipped = flipped)
		svg = svg.replace("y=\"390\"", "y=\"395\"")
		with open(self.bot.data_path + "/temp/chess_board.svg", 'w') as image:
			print(svg, file = image)
		with Image(filename = self.bot.data_path + "/temp/chess_board.svg") as img:
			img.format = "png"
			img.save(filename = self.bot.data_path + "/temp/chess_board.png")
		# asyncio.sleep(0.2)  # necessary?, wasn't even awaited
		if self.match_message:
			embed = self.match_message.embeds[0]
		else:
			embed = discord.Embed(color = self.bot.bot_color)
		chess_pgn = chess.pgn.Game.from_board(self)
		chess_pgn.headers["Site"] = "Discord"
		chess_pgn.headers["Date"] = datetime.datetime.utcnow().strftime("%Y.%m.%d")
		chess_pgn.headers["White"] = self.white_player.mention
		chess_pgn.headers["Black"] = self.black_player.mention
		embed.description = str(chess_pgn)
		# TODO: Upload into embed + delete and re-send to update?
		'''
		embed.set_image(url = self.bot.imgur_client.upload_from_path(self.bot.data_path + "/temp/chess_board.png")["link"])
		embed.set_image(url = data["data"]["img_url"])
		'''
		image_message = await self.bot.cache_channel.send(file = discord.File(self.bot.data_path + "/temp/chess_board.png"))
		embed.set_image(url = image_message.attachments[0].url)
		embed.set_footer(text = footer_text)
		if not self.match_message:
			self.match_message = await self.ctx.send(embed = embed)
		else:
			await self.match_message.edit(embed = embed)

	async def new_match_embed(self, *, flipped = None, footer_text = None):
		if flipped is None:
			flipped = not self.turn
		if footer_text is None:
			if self.is_game_over():
				footer_text = discord.Embed.Empty
			else:
				footer_text = f"It's {['black', 'white'][int(self.turn)]}'s ({[self.black_player, self.white_player][int(self.turn)]}'s) turn to move"
		if self.match_message:
			await self.match_message.delete()
		self.match_message = None
		await self.update_match_embed(flipped = flipped, footer_text = footer_text)

<|endoftext|>


<commit_before>from pyramid.events import (
    ContextFound,
    subscriber,
)
from pyramid.httpexceptions import (
    HTTPForbidden,
    HTTPInternalServerError,
    HTTPNotFound,
)
from pyramid.location import lineage
from pyramid.security import (
    Allow,
    Everyone,
    has_permission,
)
from pyramid.threadlocal import manager
from pyramid.view import view_config
from urllib import unquote
from uuid import UUID
from ..objtemplate import ObjectTemplate
from ..schema_utils import validate_request
from ..storage import (
    DBSession,
    CurrentStatement,
    Resource,
)

def includeme(config):
    config.include('.views')

def make_subrequest(request, path):
    """ Make a subrequest

    Copies request environ data for authentication.

    May be better to just pull out the resource through traversal and manually
    perform security checks.
    """
    env = request.environ.copy()
    if path and '?' in path:
        path_info, query_string = path.split('?', 1)
        path_info = unquote(path_info)
    else:
        path_info = unquote(path)
        query_string = ''
    env['PATH_INFO'] = path_info
    env['QUERY_STRING'] = query_string
    subreq = request.__class__(env, method='GET', content_type=None,
                               body=b'')
    subreq.remove_conditional_headers()
    # XXX "This does not remove headers like If-Match"
    return subreq

def embed(request, path, result=None):
    # Should really be more careful about what gets included instead.
    # Cache cut response time from ~800ms to ~420ms.
    embedded = None
    if manager.stack:
        embedded = manager.stack[0].setdefault('encoded_embedded', {})
    if result is not None:
        embedded[path] = result
        return result
    result = embedded.get(path, None)
    if result is not None:
        return result
    subreq = make_subrequest(request, path)
    subreq.override_renderer = 'null_renderer'
    try:
        result = request.invoke_subrequest(subreq)
    except HTTPNotFound:
        raise KeyError(path)
    if embedded is not None:
        embedded[path] = result
    return result

def maybe_include_embedded(request, result):
    if len(manager.stack) != 1:
        return
    embedded = manager.stack[0].get('encoded_embedded', None)
    if embedded:
        result['_embedded'] = {'resources': embedded}

def no_body_needed(request):
    # No need for request data when rendering the single page html
    return request.environ.get('encoded.format') == 'html'

def validate_item_content(context, request):
    data = request.json_body
    if isinstance(context, Item):
        schema = context.__parent__.schema
    else:
        schema = context.schema
    if schema is None:
        request.validated = data
        return
    validate_request(schema, request)

class Root(object):
    __name__ = ''
    __parent__ = None

    __acl__ = [
        (Allow, Everyone, 'list'),
        (Allow, 'group:admin', 'add'),
        (Allow, Everyone, 'view'),
        (Allow, 'group:admin', 'edit'),
        (Allow, Everyone, 'traverse'),
    ]

    def __init__(self, **properties):
        self.properties = properties
        self.collections = {}

    def __call__(self, request):
        return self

    def __getitem__(self, name):
        return self.collections[name]

    def __json__(self, request=None):
        return self.properties.copy()

    def location(self, name):
        """ Attach a collection at the location ``name``.

        Use as a decorator on Collection subclasses.
        """
        def decorate(factory):
            self.collections[name] = factory(self, name)
            return factory
        return decorate

class Collection(object):
    schema = None
    properties = None
    item_type = None
    links = {
        'self': {'href': '{collection_uri}{_uuid}', 'templated': True},
        'collection': {'href': '{collection_uri}', 'templated': True},
        'profile': {'href': '/profiles/{item_type}.json', 'templated': True},
    }
    embedded = {}

    def __init__(self, parent, name):
        self.__name__ = name
        self.__parent__ = parent
        if self.item_type is None:
            self.item_type = type(self).__name__.lower()

        merged_links = {}
        for cls in reversed(type(self).mro()):
            merged_links.update(vars(cls).get('links', {}))
        self.links = merged_links

    def item_acl(self, model):
        return None

    def __getitem__(self, name):
        try:
            name = UUID(name)
        except ValueError:
            raise KeyError(name)
        try:
            item = self.get(name)
        except KeyError:
            # Just in case we get an unexpected KeyError
            # FIXME: exception logging.
            raise HTTPInternalServerError('Traversal raised KeyError')
        if item is None:
            raise KeyError(name)
        return item

    def get(self, name, default=None):
        key = (name, self.item_type)
        session = DBSession()
        model = session.query(CurrentStatement).get(key)
        if model is not None:
            return self.make_item(model)
        return default

    def make_item(self, model):
        acl = self.item_acl(model)
        return Item(self, model, acl)

    def add(self, properties):
        rid = properties.get('_uuid', None)
        session = DBSession()
        resource = Resource({self.item_type: properties}, rid)
        session.add(resource)
        item = self.make_item(resource.data[self.item_type])
        self.after_add(item)
        return item

    def after_add(self, item):
        '''Hook for subclasses'''

@view_config(context=Collection, permission='list', request_method='GET')
def collection_list(context, request):
    nrows = request.params.get('limit', None)
    if no_body_needed(request):
        return {}
    session = DBSession()
    query = session.query(CurrentStatement).filter(CurrentStatement.predicate == context.item_type).limit(nrows)
    items = []
    for model in query.all():
        item = context.make_item(model)
        properties = item.__json__(request)
        item_uri = request.resource_path(context, item.__name__)
        embed(request, item_uri, properties)
        items.append({'href': item_uri})
    collection_uri = request.resource_path(context)
    result = {
        '_embedded': {
            'items': items,
        },
        '_links': {
            'self': {'href': collection_uri},
            'items': items,
            '/rels/actions': [
                {
                    'name': 'add-antibody',
                    'title': 'Add antibody',
                    'method': 'POST',
                    'type': 'application/json',
                    'href': collection_uri,
                }
            ],
        },
    }

    if context.properties is not None:
        result.update(context.properties)

    maybe_include_embedded(request, result)
    return result

@view_config(context=Collection, validators=(validate_item_content,), permission='add', request_method='POST')
def collection_add(context, request):
    properties = request.validated
    item = context.add(properties)
    item_uri = request.resource_path(context, item.__name__)
    request.response.status = 201
    request.response.location = item_uri
    result = {
        'result': 'success',
        '_links': {
            'profile': {'href': '/profiles/result'},
            'items': [
                {'href': item_uri},
            ],
        },
    }
    return result

@subscriber(ContextFound)
def traversal_security(event):
    """ Check traversal was permitted at each step
    """
    request = event.request
    for resource in reversed(list(lineage(request.context))):
        result = has_permission('traverse', resource, request)
        if not result:
            msg = 'Unauthorized: traversal failed permission check'
            raise HTTPForbidden(msg, result=result)

class Item(object):
    # See http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/resources.html
    def __init__(self, collection, model, acl=None):
        self.__name__ = model.rid
        self.__parent__ = collection
        self.model = model
        if acl is not None:
            self.__acl__ = acl

    @property
    def properties(self):
        return self.model.statement.object

    def __json__(self, request):
        properties = self.properties.copy()
        links = self.expand_links(properties, request)
        if links is not None:
            properties['_links'] = links
        return properties

    def expand_links(self, properties, request):
        # Expand templated links
        ns = properties.copy()
        ns['collection_uri'] = request.resource_path(self.__parent__)
        ns['item_type'] = self.model.predicate
        compiled = ObjectTemplate(self.__parent__.links)
        links = compiled(ns)
        # Embed resources
        embedded = self.__parent__.embedded
        for rel, value in links.items():
            if rel not in embedded:
                continue
            if isinstance(value, list):
                for member in value:
                    embed(request, member['href'])
            else:
                embed(request, value['href'])
        return links

@view_config(context=Item, permission='view', request_method='GET')
def item_view(context, request):
    if no_body_needed(request):
        return {}
    properties = context.__json__(request)
    maybe_include_embedded(request, properties)
    return properties

@view_config(context=Item, validators=[validate_item_content], permission='edit', request_method='POST')
def item_edit(context, request):
    uuid = context.model.rid
    collection = context.__parent__
    properties = request.validated
    properties['_uuid'] = uuid  # XXX Should this really be stored in object?
    context.model.resource[collection.item_type] = properties
    item_uri = request.resource_path(context.__parent__, context.__name__)
    request.response.status = 200
    result = {
        'result': 'success',
        '_links': {
            'profile': {'href': '/profiles/result'},
            'items': [
                {'href': item_uri},
            ],
        },
    }
    return result
<commit_msg>Give each collection its own Item class to enable specific view registration.<commit_after>from pyramid.events import (
    ContextFound,
    subscriber,
)
from pyramid.httpexceptions import (
    HTTPForbidden,
    HTTPInternalServerError,
    HTTPNotFound,
)
from pyramid.location import lineage
from pyramid.security import (
    Allow,
    Everyone,
    has_permission,
)
from pyramid.threadlocal import manager
from pyramid.view import view_config
from urllib import unquote
from uuid import UUID
from ..objtemplate import ObjectTemplate
from ..schema_utils import validate_request
from ..storage import (
    DBSession,
    CurrentStatement,
    Resource,
)

def includeme(config):
    config.include('.views')

def make_subrequest(request, path):
    """ Make a subrequest

    Copies request environ data for authentication.

    May be better to just pull out the resource through traversal and manually
    perform security checks.
    """
    env = request.environ.copy()
    if path and '?' in path:
        path_info, query_string = path.split('?', 1)
        path_info = unquote(path_info)
    else:
        path_info = unquote(path)
        query_string = ''
    env['PATH_INFO'] = path_info
    env['QUERY_STRING'] = query_string
    subreq = request.__class__(env, method='GET', content_type=None,
                               body=b'')
    subreq.remove_conditional_headers()
    # XXX "This does not remove headers like If-Match"
    return subreq

def embed(request, path, result=None):
    # Should really be more careful about what gets included instead.
    # Cache cut response time from ~800ms to ~420ms.
    embedded = None
    if manager.stack:
        embedded = manager.stack[0].setdefault('encoded_embedded', {})
    if result is not None:
        embedded[path] = result
        return result
    result = embedded.get(path, None)
    if result is not None:
        return result
    subreq = make_subrequest(request, path)
    subreq.override_renderer = 'null_renderer'
    try:
        result = request.invoke_subrequest(subreq)
    except HTTPNotFound:
        raise KeyError(path)
    if embedded is not None:
        embedded[path] = result
    return result

def maybe_include_embedded(request, result):
    if len(manager.stack) != 1:
        return
    embedded = manager.stack[0].get('encoded_embedded', None)
    if embedded:
        result['_embedded'] = {'resources': embedded}

def no_body_needed(request):
    # No need for request data when rendering the single page html
    return request.environ.get('encoded.format') == 'html'

def validate_item_content(context, request):
    data = request.json_body
    if isinstance(context, Item):
        schema = context.__parent__.schema
    else:
        schema = context.schema
    if schema is None:
        request.validated = data
        return
    validate_request(schema, request)

class Root(object):
    __name__ = ''
    __parent__ = None

    __acl__ = [
        (Allow, Everyone, 'list'),
        (Allow, 'group:admin', 'add'),
        (Allow, Everyone, 'view'),
        (Allow, 'group:admin', 'edit'),
        (Allow, Everyone, 'traverse'),
    ]

    def __init__(self, **properties):
        self.properties = properties
        self.collections = {}

    def __call__(self, request):
        return self

    def __getitem__(self, name):
        return self.collections[name]

    def __json__(self, request=None):
        return self.properties.copy()

    def location(self, name):
        """ Attach a collection at the location ``name``.

        Use as a decorator on Collection subclasses.
        """
        def decorate(factory):
            self.collections[name] = factory(self, name)
            return factory
        return decorate

class Item(object):
    # See http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/resources.html
    def __init__(self, collection, model, acl=None):
        self.__name__ = model.rid
        self.__parent__ = collection
        self.model = model
        if acl is not None:
            self.__acl__ = acl

    @property
    def properties(self):
        return self.model.statement.object

    def __json__(self, request):
        properties = self.properties.copy()
        links = self.expand_links(properties, request)
        if links is not None:
            properties['_links'] = links
        return properties

    def expand_links(self, properties, request):
        # Expand templated links
        ns = properties.copy()
        ns['collection_uri'] = request.resource_path(self.__parent__)
        ns['item_type'] = self.model.predicate
        compiled = ObjectTemplate(self.__parent__.links)
        links = compiled(ns)
        # Embed resources
        embedded = self.__parent__.embedded
        for rel, value in links.items():
            if rel not in embedded:
                continue
            if isinstance(value, list):
                for member in value:
                    embed(request, member['href'])
            else:
                embed(request, value['href'])
        return links

class CustomItemMeta(type):
    """ Give each collection its own Item class to enable
        specific view registration.
    """
    def __init__(self, name, bases, attrs):
        super(CustomItemMeta, self).__init__(name, bases, attrs)
        if 'Item' in attrs:
            return
        item_bases = tuple(base.Item for base in bases
                           if issubclass(base, Collection))
        qualname = getattr(self, '__qualname__', self.__name__)  # PY3 only
        item_attrs = {
            '__module__': self.__module__,
            '__name__': 'Item',
            '__qualname__': qualname + '.Item',
        }
        self.Item = type('Item', item_bases, item_attrs)

class Collection(object):
    __metaclass__ = CustomItemMeta
    Item = Item
    schema = None
    properties = None
    item_type = None
    links = {
        'self': {'href': '{collection_uri}{_uuid}', 'templated': True},
        'collection': {'href': '{collection_uri}', 'templated': True},
        'profile': {'href': '/profiles/{item_type}.json', 'templated': True},
    }
    embedded = {}

    def __init__(self, parent, name):
        self.__name__ = name
        self.__parent__ = parent
        if self.item_type is None:
            self.item_type = type(self).__name__.lower()

        merged_links = {}
        for cls in reversed(type(self).mro()):
            merged_links.update(vars(cls).get('links', {}))
        self.links = merged_links

    def item_acl(self, model):
        return None

    def __getitem__(self, name):
        try:
            name = UUID(name)
        except ValueError:
            raise KeyError(name)
        try:
            item = self.get(name)
        except KeyError:
            # Just in case we get an unexpected KeyError
            # FIXME: exception logging.
            raise HTTPInternalServerError('Traversal raised KeyError')
        if item is None:
            raise KeyError(name)
        return item

    def get(self, name, default=None):
        key = (name, self.item_type)
        session = DBSession()
        model = session.query(CurrentStatement).get(key)
        if model is not None:
            return self.make_item(model)
        return default

    def make_item(self, model):
        acl = self.item_acl(model)
        return self.Item(self, model, acl)

    def add(self, properties):
        rid = properties.get('_uuid', None)
        session = DBSession()
        resource = Resource({self.item_type: properties}, rid)
        session.add(resource)
        item = self.make_item(resource.data[self.item_type])
        self.after_add(item)
        return item

    def after_add(self, item):
        '''Hook for subclasses'''

@view_config(context=Collection, permission='list', request_method='GET')
def collection_list(context, request):
    nrows = request.params.get('limit', None)
    if no_body_needed(request):
        return {}
    session = DBSession()
    query = session.query(CurrentStatement).filter(CurrentStatement.predicate == context.item_type).limit(nrows)
    items = []
    for model in query.all():
        item = context.make_item(model)
        properties = item.__json__(request)
        item_uri = request.resource_path(context, item.__name__)
        embed(request, item_uri, properties)
        items.append({'href': item_uri})
    collection_uri = request.resource_path(context)
    result = {
        '_embedded': {
            'items': items,
        },
        '_links': {
            'self': {'href': collection_uri},
            'items': items,
            '/rels/actions': [
                {
                    'name': 'add-antibody',
                    'title': 'Add antibody',
                    'method': 'POST',
                    'type': 'application/json',
                    'href': collection_uri,
                }
            ],
        },
    }

    if context.properties is not None:
        result.update(context.properties)

    maybe_include_embedded(request, result)
    return result

@view_config(context=Collection, validators=(validate_item_content,), permission='add', request_method='POST')
def collection_add(context, request):
    properties = request.validated
    item = context.add(properties)
    item_uri = request.resource_path(context, item.__name__)
    request.response.status = 201
    request.response.location = item_uri
    result = {
        'result': 'success',
        '_links': {
            'profile': {'href': '/profiles/result'},
            'items': [
                {'href': item_uri},
            ],
        },
    }
    return result

@subscriber(ContextFound)
def traversal_security(event):
    """ Check traversal was permitted at each step
    """
    request = event.request
    for resource in reversed(list(lineage(request.context))):
        result = has_permission('traverse', resource, request)
        if not result:
            msg = 'Unauthorized: traversal failed permission check'
            raise HTTPForbidden(msg, result=result)

@view_config(context=Item, permission='view', request_method='GET')
def item_view(context, request):
    if no_body_needed(request):
        return {}
    properties = context.__json__(request)
    maybe_include_embedded(request, properties)
    return properties

@view_config(context=Item, validators=[validate_item_content], permission='edit', request_method='POST')
def item_edit(context, request):
    uuid = context.model.rid
    collection = context.__parent__
    properties = request.validated
    properties['_uuid'] = uuid  # XXX Should this really be stored in object?
    context.model.resource[collection.item_type] = properties
    item_uri = request.resource_path(context.__parent__, context.__name__)
    request.response.status = 200
    result = {
        'result': 'success',
        '_links': {
            'profile': {'href': '/profiles/result'},
            'items': [
                {'href': item_uri},
            ],
        },
    }
    return result
<|endoftext|>


<commit_before>"""engine.SCons.Tool.msvc

Tool-specific initialization for Microsoft Visual C/C++.

There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.

"""

#
# __COPYRIGHT__
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"

import os.path
import re
import string
import sys

import SCons.Action
import SCons.Builder
import SCons.Errors
import SCons.Platform.win32
import SCons.Tool
import SCons.Tool.msvs
import SCons.Util
import SCons.Warnings
import SCons.Scanner.RC

from MSCommon import msvc_exists, msvc_setup_env

CSuffixes = ['.c', '.C']
CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']

def validate_vars(env):
    """Validate the PCH and PCHSTOP construction variables."""
    if env.has_key('PCH') and env['PCH']:
        if not env.has_key('PCHSTOP'):
            raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
        if not SCons.Util.is_String(env['PCHSTOP']):
            raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']

def pch_emitter(target, source, env):
    """Adds the object file target."""

    validate_vars(env)

    pch = None
    obj = None

    for t in target:
        if SCons.Util.splitext(str(t))[1] == '.pch':
            pch = t
        if SCons.Util.splitext(str(t))[1] == '.obj':
            obj = t

    if not obj:
        obj = SCons.Util.splitext(str(pch))[0]+'.obj'

    target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work

    return (target, source)

def object_emitter(target, source, env, parent_emitter):
    """Sets up the PCH dependencies for an object file."""

    validate_vars(env)

    parent_emitter(target, source, env)

    if env.has_key('PCH') and env['PCH']:
        env.Depends(target, env['PCH'])

    return (target, source)

def static_object_emitter(target, source, env):
    return object_emitter(target, source, env,
                          SCons.Defaults.StaticObjectEmitter)

def shared_object_emitter(target, source, env):
    return object_emitter(target, source, env,
                          SCons.Defaults.SharedObjectEmitter)

pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR')
pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch',
                                    emitter=pch_emitter,
                                    source_scanner=SCons.Tool.SourceFileScanner)

# Logic to build .rc files into .res files (resource files)
res_scanner = SCons.Scanner.RC.RCScan()
res_action  = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
res_builder = SCons.Builder.Builder(action=res_action,
                                    src_suffix='.rc',
                                    suffix='.res',
                                    src_builder=[],
                                    source_scanner=res_scanner)

def msvc_batch_key(action, env, target, source):
    """
    Returns a key to identify unique batches of sources for compilation.

    If batching is enabled (via the $MSVC_BATCH setting), then all
    target+source pairs that use the same action, defined by the same
    environment, and have the same target and source directories, will
    be batched.

    Returning None specifies that the specified target+source should not
    be batched with other compilations.
    """
    b = env.subst('$MSVC_BATCH')
    if b in (None, '', '0'):
        # We're not using batching; return no key.
        return None
    t = target[0]
    s = source[0]
    if os.path.splitext(t.name)[0] != os.path.splitext(s.name)[0]:
        # The base names are different, so this *must* be compiled
        # separately; return no key.
        return None
    return (id(action), id(env), t.dir, s.dir)

def msvc_output_flag(target, source, env, for_signature):
    """
    Returns the correct /Fo flag for batching.

    If batching is disabled or there's only one source file, then we
    return an /Fo string that specifies the target explicitly.  Otherwise,
    we return an /Fo string that just specifies the first target's
    directory (where the Visual C/C++ compiler will put the .obj files).
    """
    b = env.subst('$MSVC_BATCH')
    if b in (None, '', '0') or len(source) == 1:
        return '/Fo$TARGET'
    else:
        # The Visual C/C++ compiler requires a \ at the end of the /Fo
        # option to indicate an output directory.  We use os.sep here so
        # that the test(s) for this can be run on non-Windows systems
        # without having a hard-coded backslash mess up command-line
        # argument parsing.
        return '/Fo${TARGET.dir}' + os.sep

CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR",
                              batch_key=msvc_batch_key,
                              targets='$CHANGED_TARGETS')
ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR",
                                batch_key=msvc_batch_key,
                                targets='$CHANGED_TARGETS')
CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR",
                                batch_key=msvc_batch_key,
                                targets='$CHANGED_TARGETS')
ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR",
                                  batch_key=msvc_batch_key,
                                  targets='$CHANGED_TARGETS')

def generate(env):
    """Add Builders and construction variables for MSVC++ to an Environment."""
    static_obj, shared_obj = SCons.Tool.createObjBuilders(env)

    # TODO(batch):  shouldn't reach in to cmdgen this way; necessary
    # for now to bypass the checks in Builder.DictCmdGenerator.__call__()
    # and allow .cc and .cpp to be compiled in the same command line.
    static_obj.cmdgen.source_ext_match = False
    shared_obj.cmdgen.source_ext_match = False

    for suffix in CSuffixes:
        static_obj.add_action(suffix, CAction)
        shared_obj.add_action(suffix, ShCAction)
        static_obj.add_emitter(suffix, static_object_emitter)
        shared_obj.add_emitter(suffix, shared_object_emitter)

    for suffix in CXXSuffixes:
        static_obj.add_action(suffix, CXXAction)
        shared_obj.add_action(suffix, ShCXXAction)
        static_obj.add_emitter(suffix, static_object_emitter)
        shared_obj.add_emitter(suffix, shared_object_emitter)

    env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}'])
    env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'])
    env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag
    env['_CCCOMCOM']  = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS'
    env['CC']         = 'cl'
    env['CCFLAGS']    = SCons.Util.CLVar('/nologo')
    env['CFLAGS']     = SCons.Util.CLVar('')
    env['CCCOM']      = '$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM'
    env['SHCC']       = '$CC'
    env['SHCCFLAGS']  = SCons.Util.CLVar('$CCFLAGS')
    env['SHCFLAGS']   = SCons.Util.CLVar('$CFLAGS')
    env['SHCCCOM']    = '$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM'
    env['CXX']        = '$CC'
    env['CXXFLAGS']   = SCons.Util.CLVar('$( /TP $)')
    env['CXXCOM']     = '$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM'
    env['SHCXX']      = '$CXX'
    env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
    env['SHCXXCOM']   = '$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM'
    env['CPPDEFPREFIX']  = '/D'
    env['CPPDEFSUFFIX']  = ''
    env['INCPREFIX']  = '/I'
    env['INCSUFFIX']  = ''
#    env.Append(OBJEMITTER = [static_object_emitter])
#    env.Append(SHOBJEMITTER = [shared_object_emitter])
    env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1

    env['RC'] = 'rc'
    env['RCFLAGS'] = SCons.Util.CLVar('')
    env['RCSUFFIXES']=['.rc','.rc2']
    env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
    env['BUILDERS']['RES'] = res_builder
    env['OBJPREFIX']      = ''
    env['OBJSUFFIX']      = '.obj'
    env['SHOBJPREFIX']    = '$OBJPREFIX'
    env['SHOBJSUFFIX']    = '$OBJSUFFIX'

    # Set-up ms tools paths for default version
    msvc_setup_env(env)

    import mssdk
    mssdk.generate(env)

    env['CFILESUFFIX'] = '.c'
    env['CXXFILESUFFIX'] = '.cc'

    env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}'])
    env['PCHCOM'] = '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS'
    env['BUILDERS']['PCH'] = pch_builder

    if not env.has_key('ENV'):
        env['ENV'] = {}
    if not env['ENV'].has_key('SystemRoot'):    # required for dlls in the winsxs folders
        env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root()

def exists(env):
    return msvc_exists('cl')

# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=4 shiftwidth=4:
<commit_msg>Issue 2481:  Fix the msvc_exists() call in Tool/msvc.py.<commit_after>"""engine.SCons.Tool.msvc

Tool-specific initialization for Microsoft Visual C/C++.

There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.

"""

#
# __COPYRIGHT__
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"

import os.path
import re
import string
import sys

import SCons.Action
import SCons.Builder
import SCons.Errors
import SCons.Platform.win32
import SCons.Tool
import SCons.Tool.msvs
import SCons.Util
import SCons.Warnings
import SCons.Scanner.RC

from MSCommon import msvc_exists, msvc_setup_env

CSuffixes = ['.c', '.C']
CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']

def validate_vars(env):
    """Validate the PCH and PCHSTOP construction variables."""
    if env.has_key('PCH') and env['PCH']:
        if not env.has_key('PCHSTOP'):
            raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
        if not SCons.Util.is_String(env['PCHSTOP']):
            raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']

def pch_emitter(target, source, env):
    """Adds the object file target."""

    validate_vars(env)

    pch = None
    obj = None

    for t in target:
        if SCons.Util.splitext(str(t))[1] == '.pch':
            pch = t
        if SCons.Util.splitext(str(t))[1] == '.obj':
            obj = t

    if not obj:
        obj = SCons.Util.splitext(str(pch))[0]+'.obj'

    target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work

    return (target, source)

def object_emitter(target, source, env, parent_emitter):
    """Sets up the PCH dependencies for an object file."""

    validate_vars(env)

    parent_emitter(target, source, env)

    if env.has_key('PCH') and env['PCH']:
        env.Depends(target, env['PCH'])

    return (target, source)

def static_object_emitter(target, source, env):
    return object_emitter(target, source, env,
                          SCons.Defaults.StaticObjectEmitter)

def shared_object_emitter(target, source, env):
    return object_emitter(target, source, env,
                          SCons.Defaults.SharedObjectEmitter)

pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR')
pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch',
                                    emitter=pch_emitter,
                                    source_scanner=SCons.Tool.SourceFileScanner)

# Logic to build .rc files into .res files (resource files)
res_scanner = SCons.Scanner.RC.RCScan()
res_action  = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
res_builder = SCons.Builder.Builder(action=res_action,
                                    src_suffix='.rc',
                                    suffix='.res',
                                    src_builder=[],
                                    source_scanner=res_scanner)

def msvc_batch_key(action, env, target, source):
    """
    Returns a key to identify unique batches of sources for compilation.

    If batching is enabled (via the $MSVC_BATCH setting), then all
    target+source pairs that use the same action, defined by the same
    environment, and have the same target and source directories, will
    be batched.

    Returning None specifies that the specified target+source should not
    be batched with other compilations.
    """
    b = env.subst('$MSVC_BATCH')
    if b in (None, '', '0'):
        # We're not using batching; return no key.
        return None
    t = target[0]
    s = source[0]
    if os.path.splitext(t.name)[0] != os.path.splitext(s.name)[0]:
        # The base names are different, so this *must* be compiled
        # separately; return no key.
        return None
    return (id(action), id(env), t.dir, s.dir)

def msvc_output_flag(target, source, env, for_signature):
    """
    Returns the correct /Fo flag for batching.

    If batching is disabled or there's only one source file, then we
    return an /Fo string that specifies the target explicitly.  Otherwise,
    we return an /Fo string that just specifies the first target's
    directory (where the Visual C/C++ compiler will put the .obj files).
    """
    b = env.subst('$MSVC_BATCH')
    if b in (None, '', '0') or len(source) == 1:
        return '/Fo$TARGET'
    else:
        # The Visual C/C++ compiler requires a \ at the end of the /Fo
        # option to indicate an output directory.  We use os.sep here so
        # that the test(s) for this can be run on non-Windows systems
        # without having a hard-coded backslash mess up command-line
        # argument parsing.
        return '/Fo${TARGET.dir}' + os.sep

CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR",
                              batch_key=msvc_batch_key,
                              targets='$CHANGED_TARGETS')
ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR",
                                batch_key=msvc_batch_key,
                                targets='$CHANGED_TARGETS')
CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR",
                                batch_key=msvc_batch_key,
                                targets='$CHANGED_TARGETS')
ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR",
                                  batch_key=msvc_batch_key,
                                  targets='$CHANGED_TARGETS')

def generate(env):
    """Add Builders and construction variables for MSVC++ to an Environment."""
    static_obj, shared_obj = SCons.Tool.createObjBuilders(env)

    # TODO(batch):  shouldn't reach in to cmdgen this way; necessary
    # for now to bypass the checks in Builder.DictCmdGenerator.__call__()
    # and allow .cc and .cpp to be compiled in the same command line.
    static_obj.cmdgen.source_ext_match = False
    shared_obj.cmdgen.source_ext_match = False

    for suffix in CSuffixes:
        static_obj.add_action(suffix, CAction)
        shared_obj.add_action(suffix, ShCAction)
        static_obj.add_emitter(suffix, static_object_emitter)
        shared_obj.add_emitter(suffix, shared_object_emitter)

    for suffix in CXXSuffixes:
        static_obj.add_action(suffix, CXXAction)
        shared_obj.add_action(suffix, ShCXXAction)
        static_obj.add_emitter(suffix, static_object_emitter)
        shared_obj.add_emitter(suffix, shared_object_emitter)

    env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}'])
    env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'])
    env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag
    env['_CCCOMCOM']  = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS'
    env['CC']         = 'cl'
    env['CCFLAGS']    = SCons.Util.CLVar('/nologo')
    env['CFLAGS']     = SCons.Util.CLVar('')
    env['CCCOM']      = '$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM'
    env['SHCC']       = '$CC'
    env['SHCCFLAGS']  = SCons.Util.CLVar('$CCFLAGS')
    env['SHCFLAGS']   = SCons.Util.CLVar('$CFLAGS')
    env['SHCCCOM']    = '$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM'
    env['CXX']        = '$CC'
    env['CXXFLAGS']   = SCons.Util.CLVar('$( /TP $)')
    env['CXXCOM']     = '$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM'
    env['SHCXX']      = '$CXX'
    env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
    env['SHCXXCOM']   = '$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM'
    env['CPPDEFPREFIX']  = '/D'
    env['CPPDEFSUFFIX']  = ''
    env['INCPREFIX']  = '/I'
    env['INCSUFFIX']  = ''
#    env.Append(OBJEMITTER = [static_object_emitter])
#    env.Append(SHOBJEMITTER = [shared_object_emitter])
    env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1

    env['RC'] = 'rc'
    env['RCFLAGS'] = SCons.Util.CLVar('')
    env['RCSUFFIXES']=['.rc','.rc2']
    env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
    env['BUILDERS']['RES'] = res_builder
    env['OBJPREFIX']      = ''
    env['OBJSUFFIX']      = '.obj'
    env['SHOBJPREFIX']    = '$OBJPREFIX'
    env['SHOBJSUFFIX']    = '$OBJSUFFIX'

    # Set-up ms tools paths for default version
    msvc_setup_env(env)

    import mssdk
    mssdk.generate(env)

    env['CFILESUFFIX'] = '.c'
    env['CXXFILESUFFIX'] = '.cc'

    env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}'])
    env['PCHCOM'] = '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS'
    env['BUILDERS']['PCH'] = pch_builder

    if not env.has_key('ENV'):
        env['ENV'] = {}
    if not env['ENV'].has_key('SystemRoot'):    # required for dlls in the winsxs folders
        env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root()

def exists(env):
    return msvc_exists()

# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=4 shiftwidth=4:
<|endoftext|>


<commit_before>from ConfigParser import SafeConfigParser

from Option import Option
from Section import Section

class EasyConfig(object):
    """
        This class is a wrapper for python's SafeConfigParser. The purpose of
        this class is to allow for more convenient use of the ConfigParser,
        along with adding built-in support for list type.

        Additionally, the class handle the type conversion implicitly inside
        it, so config options are retrieved with their appropriate type.

        Accessing configuration options with the class is performed as if the
        options where class members, so, for example, if the configuration file
        as the following section and item:

        [SomeSection]
        some_item = some_value

        Then, accessing the item will look like that:

        conf = EasyConfig("path_to_config.ini")
        print conf.SomeSection.some_item # Will print "some_value"
        conf.SomeSection.some_item = 3.14
        print conf.SomeSection.some_item # Will print 3.14
        conf.SomeSection.some_item += 0.01
        print conf.SomeSection.some_item # Will print 3.15

        A list config item is written as if it was a python's legit list deler-
        ation. For example:

        [SomeSection]
        some_list = [1, 2, 3]
    """

    def __init__(self, config):
        """
            The class is initialized with a path to a configuration file passed
            via config, or a file-like object.
        """
        self.parser = SafeConfigParser()

        if isinstance(config, basestring):
            self.config_path = config
            parser = open(self.config_path, "r")
        else:
            self.config_path = None

        self.parser.readfp(config)

        for section in self.parser.sections():
            options = self.parser.items(section)
            options = map(lambda op: Option(*op), options)
            self.add_section(Section(section, options))

    def __iter__(self):
        """ Iterate over all the section in the configuration. """
        for section in self.__dict__.values():
            if isinstance(section, Section):
                yield section

    def __iadd__(self, section):
        """
            If section is of type Section, adds the section to the
            config (if it's not already present). Otherwise, the operator does
            nothing.
        """
        self.add_section(section)
        return self

    def add_section(self, section):
        """
            Adds a new section to the config. If section is str, it's converted
            to Section object. If section is Section, it's left untouched.

            If a section with the same name does not already exists in the
            config, it's added, and the function returns True. If such section
            already exists, the call is ignored, and the function return False.
        """
        if isinstance(section, Section):
            section_name = section.name
        elif isinstance(section, str):
            section_name = section
            section = Section(section)
        else:
            raise TypeError("section should be either str or Section")

        if not self.__dict__.has_key(section_name):
            setattr(self, section_name, section)
            return True
        else:
            return False

    def save(self, config = None):
        """
            Will pass the configuration to the ConfigParser module, and save
            the configuration.

            If config is none, the configuration will be saved in the path
            with which the object was initialized.

            If config is a path to a file, the configuration will be saved in
            that file. If config is a file-like object, it will be written to
            it.
        """
        if not config:
            if not self.config_path:
                raise RuntimeError(
                    "No output defined (either path or file-like object.")
            config = open(self.config_path, "w")
        elif isinstance(config, basestring):
            config = open(config, "w")

        self.update_parser()
        self.parser.write(config)

    def update_parser(self):
        """
            Updates the state of the internal ConfigParser.
        """
        for section in self:
            if not self.parser.has_section(str(section)):
                self.parser.add_section(str(section))
            for option in section:
                self.parser.set(str(section), str(option), option.value_str)

    def upgrade(self, other_easy_config):
        """
            Adds all the values from the other config to the current config. If
            the other config contains options that are already present in the
            current config, the new options are ignored.
        """
        for other_section in other_easy_config:
            # False means that such section name already exists.
            if not self.add_section(other_section):
                for other_option in other_section:
                    section = getattr(self, other_section.name)
                    section += other_option

<commit_msg>Fixed initialization type when passing a path to the config file and not file-like obj.<commit_after>from ConfigParser import SafeConfigParser

from Option import Option
from Section import Section

class EasyConfig(object):
    """
        This class is a wrapper for python's SafeConfigParser. The purpose of
        this class is to allow for more convenient use of the ConfigParser,
        along with adding built-in support for list type.

        Additionally, the class handle the type conversion implicitly inside
        it, so config options are retrieved with their appropriate type.

        Accessing configuration options with the class is performed as if the
        options where class members, so, for example, if the configuration file
        as the following section and item:

        [SomeSection]
        some_item = some_value

        Then, accessing the item will look like that:

        conf = EasyConfig("path_to_config.ini")
        print conf.SomeSection.some_item # Will print "some_value"
        conf.SomeSection.some_item = 3.14
        print conf.SomeSection.some_item # Will print 3.14
        conf.SomeSection.some_item += 0.01
        print conf.SomeSection.some_item # Will print 3.15

        A list config item is written as if it was a python's legit list deler-
        ation. For example:

        [SomeSection]
        some_list = [1, 2, 3]
    """

    def __init__(self, config):
        """
            The class is initialized with a path to a configuration file passed
            via config, or a file-like object.
        """
        self.parser = SafeConfigParser()

        if isinstance(config, basestring):
            self.config_path = config
            config = open(self.config_path, "r")
        else:
            self.config_path = None

        self.parser.readfp(config)

        for section in self.parser.sections():
            options = self.parser.items(section)
            options = map(lambda op: Option(*op), options)
            self.add_section(Section(section, options))

    def __iter__(self):
        """ Iterate over all the section in the configuration. """
        for section in self.__dict__.values():
            if isinstance(section, Section):
                yield section

    def __iadd__(self, section):
        """
            If section is of type Section, adds the section to the
            config (if it's not already present). Otherwise, the operator does
            nothing.
        """
        self.add_section(section)
        return self

    def add_section(self, section):
        """
            Adds a new section to the config. If section is str, it's converted
            to Section object. If section is Section, it's left untouched.

            If a section with the same name does not already exists in the
            config, it's added, and the function returns True. If such section
            already exists, the call is ignored, and the function return False.
        """
        if isinstance(section, Section):
            section_name = section.name
        elif isinstance(section, str):
            section_name = section
            section = Section(section)
        else:
            raise TypeError("section should be either str or Section")

        if not self.__dict__.has_key(section_name):
            setattr(self, section_name, section)
            return True
        else:
            return False

    def save(self, config = None):
        """
            Will pass the configuration to the ConfigParser module, and save
            the configuration.

            If config is none, the configuration will be saved in the path
            with which the object was initialized.

            If config is a path to a file, the configuration will be saved in
            that file. If config is a file-like object, it will be written to
            it.
        """
        if not config:
            if not self.config_path:
                raise RuntimeError(
                    "No output defined (either path or file-like object.")
            config = open(self.config_path, "w")
        elif isinstance(config, basestring):
            config = open(config, "w")

        self.update_parser()
        self.parser.write(config)

    def update_parser(self):
        """
            Updates the state of the internal ConfigParser.
        """
        for section in self:
            if not self.parser.has_section(str(section)):
                self.parser.add_section(str(section))
            for option in section:
                self.parser.set(str(section), str(option), option.value_str)

    def upgrade(self, other_easy_config):
        """
            Adds all the values from the other config to the current config. If
            the other config contains options that are already present in the
            current config, the new options are ignored.
        """
        for other_section in other_easy_config:
            # False means that such section name already exists.
            if not self.add_section(other_section):
                for other_option in other_section:
                    section = getattr(self, other_section.name)
                    section += other_option

<|endoftext|>