From 0305a6ce3b34c888584eb8aa29dd60a40d94841b Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Sat, 28 Aug 2010 03:27:14 +0200 Subject: [PATCH] generalized Group entity updated proxies renamed Definition class improved JSON definitions of entities and properties --- backend/lib/Controller/ChannelController.php | 26 +-- backend/lib/Controller/DataController.php | 13 +- backend/lib/Controller/GroupController.php | 14 +- ...erpreter.php => AggregatorInterpreter.php} | 22 +- backend/lib/Interpreter/Interpreter.php | 6 +- backend/lib/Interpreter/MeterInterpreter.php | 2 +- .../DataAggregationIterator.php | 2 +- .../DataIterator.php | 2 +- .../lib/Model/{Group.php => Aggregator.php} | 31 +-- backend/lib/Model/Channel.php | 47 +--- backend/lib/Model/Data.php | 2 +- backend/lib/Model/Entity.php | 130 +++++++---- backend/lib/Model/Property.php | 111 ++-------- backend/lib/Model/PropertyDefinition.php | 107 +++++++++ .../VolkszaehlerModelAggregatorProxy.php} | 74 ++----- .../VolkszaehlerModelChannelProxy.php | 70 ++---- .../VolkszaehlerModelDataProxy.php | 2 +- .../VolkszaehlerModelEntityProxy.php | 16 +- .../VolkszaehlerModelPropertyProxy.php | 14 +- .../VolkszaehlerModelTokenProxy.php | 2 +- .../{JSONDefinition.php => Definition.php} | 0 backend/lib/View/CSV.php | 2 +- backend/lib/View/HTTP/Request.php | 6 +- backend/lib/View/JSON.php | 28 +-- backend/lib/View/JpGraph.php | 29 +-- backend/lib/View/PlainText.php | 4 +- backend/lib/View/View.php | 9 +- backend/lib/View/XML.php | 28 +-- share/entities.json | 31 ++- share/properties.json | 205 +++++++++++++++++- 30 files changed, 608 insertions(+), 427 deletions(-) rename backend/lib/Interpreter/{GroupInterpreter.php => AggregatorInterpreter.php} (82%) rename backend/lib/{Interpreter => Iterator}/DataAggregationIterator.php (98%) rename backend/lib/{Interpreter => Iterator}/DataIterator.php (98%) rename backend/lib/Model/{Group.php => Aggregator.php} (73%) create mode 100644 backend/lib/Model/PropertyDefinition.php rename backend/lib/Model/{Proxies/VolkszaehlerModelGroupProxy.php => Proxy/VolkszaehlerModelAggregatorProxy.php} (61%) rename backend/lib/Model/{Proxies => Proxy}/VolkszaehlerModelChannelProxy.php (65%) rename backend/lib/Model/{Proxies => Proxy}/VolkszaehlerModelDataProxy.php (97%) rename backend/lib/Model/{Proxies => Proxy}/VolkszaehlerModelEntityProxy.php (79%) rename backend/lib/Model/{Proxies => Proxy}/VolkszaehlerModelPropertyProxy.php (83%) rename backend/lib/Model/{Proxies => Proxy}/VolkszaehlerModelTokenProxy.php (96%) rename backend/lib/Util/{JSONDefinition.php => Definition.php} (100%) diff --git a/backend/lib/Controller/ChannelController.php b/backend/lib/Controller/ChannelController.php index 375c537..a095c8d 100644 --- a/backend/lib/Controller/ChannelController.php +++ b/backend/lib/Controller/ChannelController.php @@ -23,7 +23,7 @@ namespace Volkszaehler\Controller; -use \Volkszaehler\Model; +use Volkszaehler\Model; /** * Channel controller @@ -43,15 +43,7 @@ class ChannelController extends Controller { $dql = 'SELECT c, p FROM Volkszaehler\Model\Channel c LEFT JOIN c.properties p'; if ($uuid = $this->view->request->getParameter('uuid')) { - // TODO add conditions - } - - if ($ugid = $this->view->request->getParameter('ugid')) { - // TODO add conditions - } - - if ($indicator = $this->view->request->getParameter('indicator')) { - // TODO add conditions + $dql .= ' WHERE c.uuid = \'' . $uuid . '\''; } $q = $this->em->createQuery($dql); @@ -64,17 +56,17 @@ class ChannelController extends Controller { /** * Add channel - * - * @todo validate input and throw exceptions */ public function add() { - $channel = new Model\Channel($this->view->request->getParameter('indicator')); + $properties = array(); - $channel->setName($this->view->request->getParameter('name')); - $channel->setDescription($this->view->request->getParameter('description')); + foreach ($this->view->request->getParameters() as $parameter => $value) { + if (Model\PropertyDefinition::exists($parameter)) { + $properties[] = new Model\Property($parameter, $value); + } + } - $channel->setResolution($this->view->request->getParameter('resolution')); - $channel->setCost($this->view->request->getParameter('cost')); + $channel = new Model\Channel($this->view->request->getParameter('type'), $properties); $this->em->persist($channel); $this->em->flush(); diff --git a/backend/lib/Controller/DataController.php b/backend/lib/Controller/DataController.php index cdda1e1..f48ba9d 100644 --- a/backend/lib/Controller/DataController.php +++ b/backend/lib/Controller/DataController.php @@ -42,14 +42,11 @@ class DataController extends Controller { * @todo use uuids for groups or channels */ public function get() { - if ($ucid = $this->view->request->getParameter('ucid')) { - $entity = $this->em->getRepository('Volkszaehler\Model\Channel')->findOneBy(array('uuid' => $ucid)); - } - elseif ($ugid = $this->view->request->getParameter('ugid')) { - $entity = $this->em->getRepository('Volkszaehler\Model\Group')->findOneBy(array('uuid' => $ugid)); + if ($uuid = $this->view->request->getParameter('uuid')) { + $entity = $this->em->getRepository('Volkszaehler\Model\Entity')->findOneBy(array('uuid' => $uuid)); } else { - throw new \Exception('you have to specifiy an ugid or ucid paramter'); + throw new \Exception('you have to specifiy the uuid parameter'); } if ($entity === FALSE) { @@ -62,8 +59,8 @@ class DataController extends Controller { $data = $entity->getInterpreter($this->em, $from, $to)->getValues($groupBy); - if ($entity instanceof Model\Group) { - $this->view->addGroup($entity, $data); + if ($entity instanceof Model\Aggregator) { + $this->view->addAggregator($entity, $data); } elseif ($entity instanceof Model\Channel) { $this->view->addChannel($entity, $data); diff --git a/backend/lib/Controller/GroupController.php b/backend/lib/Controller/GroupController.php index 388997c..7f024ba 100644 --- a/backend/lib/Controller/GroupController.php +++ b/backend/lib/Controller/GroupController.php @@ -39,7 +39,7 @@ class GroupController extends Controller { * @todo filter to root groups when using recursion */ public function get() { - $dql = 'SELECT g, c, d FROM Volkszaehler\Model\Group g LEFT JOIN g.children c LEFT JOIN g.channels d'; + $dql = 'SELECT g, c, d FROM Volkszaehler\Model\Aggregator g LEFT JOIN g.children c LEFT JOIN g.channels d'; // TODO fix this (depending on DDC-719) if ($recursion = $this->view->request->getParameter('recursive')) { @@ -58,7 +58,7 @@ class GroupController extends Controller { $groups = $q->getResult(); foreach ($groups as $group) { - $this->view->addGroup($group, $recursion); + $this->view->addAggregator($group, $recursion); } } @@ -69,19 +69,19 @@ class GroupController extends Controller { */ public function add() { $ugid = $this->view->request->getParameter('ugid'); - $parent = $this->em->getRepository('Volkszaehler\Model\Group')->findOneBy(array('uuid' => $ugid)); + $parent = $this->em->getRepository('Volkszaehler\Model\Aggregator')->findOneBy(array('uuid' => $ugid)); if ($parent == FALSE) { throw new \Exception('every group needs a parent'); } - $group = new Model\Group(); + $group = new Model\Aggregator(); $group->setName($this->view->request->getParameter('name')); $group->setDescription($this->view->request->getParameter('description')); $this->em->persist($group); - $parent->addGroup($group); + $parent->addAggregator($group); $this->em->flush(); @@ -93,7 +93,7 @@ class GroupController extends Controller { */ public function delete() { $ugid = $this->view->request->getParameter('ugid'); - $group = $this->em->getRepository('Volkszaehler\Model\Group')->findOneBy(array('uuid' => $ugid)); + $group = $this->em->getRepository('Volkszaehler\Model\Aggregator')->findOneBy(array('uuid' => $ugid)); $this->em->remove($group); $this->em->flush(); @@ -102,7 +102,7 @@ class GroupController extends Controller { /** * edit group properties * - * @todo implement Controller\Group::edit() + * @todo implement Controller\Aggregator::edit() */ public function edit() { diff --git a/backend/lib/Interpreter/GroupInterpreter.php b/backend/lib/Interpreter/AggregatorInterpreter.php similarity index 82% rename from backend/lib/Interpreter/GroupInterpreter.php rename to backend/lib/Interpreter/AggregatorInterpreter.php index 607a6bf..911c5c1 100644 --- a/backend/lib/Interpreter/GroupInterpreter.php +++ b/backend/lib/Interpreter/AggregatorInterpreter.php @@ -27,29 +27,35 @@ use Doctrine\ORM; use Volkszaehler\Model; /** - * Group interpreter + * Interpreter too aggregate several other Channels or Aggregators * - * The GroupInterpreter is used to aggregate multiple channels with the same + * The AggregatorInterpreter is used to aggregate multiple channels with the same * indicator * * @author Steffen Vogel * @package default * */ -class GroupInterpreter { - protected $channelInterpreter; +class AggregatorInterpreter { + /** + * @var array of Interpreter + */ + protected $channelInterpreter = array(); /** * Constructor * - * @param Model\Group $group should only contain channels of the same indicator + * @param Model\Aggregator $group should only contain channels of the same indicator * @param ORM\EntityManager $em * @param integer $from timestamp in ms since 1970 * @param integer $to timestamp in ms since 1970 + * @todo handle channels in nested aggregators */ - public function __construct(Model\Group $group, ORM\EntityManager $em, $from, $to) { - foreach ($group->getChannels() as $channel) { - if (isset($indicator) && $indicator != $channel->getIndicator) { + public function __construct(Model\Aggregator $aggregator, ORM\EntityManager $em, $from, $to) { + $indicator = NULL; + + foreach ($aggregator->getChannels() as $channels) { + if (isset($indicator) && $indicator != $channel->getIndicator()) { throw new \Exception('we only can aggregate channels of the same indicator'); } else { diff --git a/backend/lib/Interpreter/Interpreter.php b/backend/lib/Interpreter/Interpreter.php index b833d5b..448d83c 100644 --- a/backend/lib/Interpreter/Interpreter.php +++ b/backend/lib/Interpreter/Interpreter.php @@ -29,6 +29,8 @@ namespace Volkszaehler\Interpreter; * @author Steffen Vogel * */ +use Volkszaehler\Iterator; + use Volkszaehler; use Doctrine\ORM\Query; @@ -106,11 +108,11 @@ abstract class Interpreter implements InterpreterInterface { // return iterators if ($sqlGroupBy || is_null($groupBy)) { // aggregation by sql or skip it - return new DataIterator($stmt, $rowCount); + return new Iterator\DataIterator($stmt, $rowCount); } elseif (is_numeric($groupBy) ) { // aggregation by php $tuples = (int) $groupBy; - return new DataAggregationIterator($stmt, $rowCount, $tuples); + return new Iterator\DataAggregationIterator($stmt, $rowCount, $tuples); } else { throw new \Exception('invalid groupBy parameter'); diff --git a/backend/lib/Interpreter/MeterInterpreter.php b/backend/lib/Interpreter/MeterInterpreter.php index afd4cbf..735b478 100644 --- a/backend/lib/Interpreter/MeterInterpreter.php +++ b/backend/lib/Interpreter/MeterInterpreter.php @@ -131,7 +131,7 @@ class MeterInterpreter extends Interpreter { return array( (int) ($next[0] - $delta / 2), // timestamp - $next[1] * (3600000 / (($this->channel->getResolution() / 1000) * $delta)), // value + $next[1] * (3600000 / (($this->channel->getProperty('resolution')->getValue() / 1000) * $delta)), // value (isset($next[2])) ? $next[2] : 1 ); } diff --git a/backend/lib/Interpreter/DataAggregationIterator.php b/backend/lib/Iterator/DataAggregationIterator.php similarity index 98% rename from backend/lib/Interpreter/DataAggregationIterator.php rename to backend/lib/Iterator/DataAggregationIterator.php index 635bbe3..63c6333 100644 --- a/backend/lib/Interpreter/DataAggregationIterator.php +++ b/backend/lib/Iterator/DataAggregationIterator.php @@ -21,7 +21,7 @@ * along with volkszaehler.org. If not, see . */ -namespace Volkszaehler\Interpreter; +namespace Volkszaehler\Iterator; use Doctrine\DBAL; diff --git a/backend/lib/Interpreter/DataIterator.php b/backend/lib/Iterator/DataIterator.php similarity index 98% rename from backend/lib/Interpreter/DataIterator.php rename to backend/lib/Iterator/DataIterator.php index 17257dd..07dd091 100644 --- a/backend/lib/Interpreter/DataIterator.php +++ b/backend/lib/Iterator/DataIterator.php @@ -21,7 +21,7 @@ * along with volkszaehler.org. If not, see . */ -namespace Volkszaehler\Interpreter; +namespace Volkszaehler\Iterator; use Doctrine\DBAL; diff --git a/backend/lib/Model/Group.php b/backend/lib/Model/Aggregator.php similarity index 73% rename from backend/lib/Model/Group.php rename to backend/lib/Model/Aggregator.php index 5ac2968..eff1c0e 100644 --- a/backend/lib/Model/Group.php +++ b/backend/lib/Model/Aggregator.php @@ -29,14 +29,15 @@ use Doctrine\Common\Collections; use Doctrine\Common\Collections\ArrayCollection; /** - * Group entity + * Aggregator entity * * @author Steffen Vogel * @package default + * @todo use nested sets: http://github.com/blt04/doctrine2-nestedset * * @Entity */ -class Group extends Entity { +class Aggregator extends Entity { /** * @ManyToMany(targetEntity="Channel", inversedBy="groups") * @JoinTable(name="groups_channel", @@ -47,7 +48,7 @@ class Group extends Entity { protected $channels = NULL; /** - * @ManyToMany(targetEntity="Group", inversedBy="parents") + * @ManyToMany(targetEntity="Aggregator", inversedBy="parents") * @JoinTable(name="groups_groups", * joinColumns={@JoinColumn(name="parent_id", referencedColumnName="id")}, * inverseJoinColumns={@JoinColumn(name="child_id", referencedColumnName="id")} @@ -56,7 +57,7 @@ class Group extends Entity { protected $children = NULL; /** - * @ManyToMany(targetEntity="Group", mappedBy="children") + * @ManyToMany(targetEntity="Aggregator", mappedBy="children") */ protected $parents = NULL; @@ -74,16 +75,16 @@ class Group extends Entity { /** * Adds group as new child * - * @param Group $child + * @param Aggregator $child * @todo check against endless recursion * @todo check if the group is already member of the group * @todo add bidirectional association */ - public function addGroup(Group $child) { + public function addAggregator(Aggregator $child) { $this->children->add($child); } - public function removeGroup(Group $child) { + public function removeAggregator(Aggregator $child) { $this->children->removeElement($child); } @@ -101,22 +102,6 @@ class Group extends Entity { public function removeChannel(Channel $child) { $this->channels->removeElement($child); } - - /** - * Getter & setter - * - * @todo change to new property model - */ - public function getName() { return $this->name; } - public function setName($name) { $this->name = $name; } - public function getDescription() { return $this->description; } - public function setDescription($description) { $this->description = $description; } - - public function getChildren() { return $this->children->toArray(); } - public function getParents() { return $this->parents->toArray(); } - public function getChannels() { return $this->channels->toArray(); } - - public function getInterpreter(ORM\EntityManager $em) { return new Interpreter\GroupInterpreter($this, $em); } } ?> \ No newline at end of file diff --git a/backend/lib/Model/Channel.php b/backend/lib/Model/Channel.php index 3698e3d..5739114 100644 --- a/backend/lib/Model/Channel.php +++ b/backend/lib/Model/Channel.php @@ -40,14 +40,14 @@ class Channel extends Entity { */ protected $data = NULL; - /** @ManyToMany(targetEntity="Group", mappedBy="channels") */ - protected $groups; + /** @ManyToMany(targetEntity="Aggregator", mappedBy="channels") */ + protected $aggregators; /** * Constructor */ - public function __construct($properties = array()) { - parent::__construct($properties); + public function __construct($type, $properties = array()) { + parent::__construct($type, $properties); $this->data = new ArrayCollection(); $this->groups = new ArrayCollection(); @@ -60,45 +60,6 @@ class Channel extends Entity { public function addData(\Volkszaehler\Model\Data $data) { $this->data->add($data); } - - /** - * Obtain channel interpreter to obtain data and statistical information for a given time interval - * - * @param Doctrine\ORM\EntityManager $em - * @param integer $from timestamp in ms since 1970 - * @param integer $to timestamp in ms since 1970 - * @return Interpreter - */ - public function getInterpreter(\Doctrine\ORM\EntityManager $em, $from, $to) { - $interpreterClassName = 'Volkszaehler\Interpreter\\' . ucfirst($this->getType()) . 'Interpreter'; - return new $interpreterClassName($this, $em, $from, $to); - } - - /** - * Validate - * - * @PrePersist @PreUpdate - */ - protected function validate() { - if ($this->getResolution() <= 0 && $this->getType() == 'meter') { - throw new Exception('resolution has to be a positive integer'); - } - } - - /** - * getter & setter - * - * @todo change to new property model - */ - public function getName() { return $this->name; } - public function setName($name) { $this->name = $name; } - public function getDescription() { return $this->description; } - public function setDescription($description) { $this->description = $description; } - - public function getResolution() { return $this->resolution; } - public function setResolution($resolution) { $this->resolution = $resolution; } - public function getCost() { return $this->cost; } - public function setCost($cost) { $this->cost = $cost; } } ?> diff --git a/backend/lib/Model/Data.php b/backend/lib/Model/Data.php index 4a606da..f71199c 100644 --- a/backend/lib/Model/Data.php +++ b/backend/lib/Model/Data.php @@ -46,7 +46,7 @@ class Data { protected $id; /** - * ending timestamp of period in ms since 1970 + * Ending timestamp of period in ms since 1970 * * @Column(type="bigint") */ diff --git a/backend/lib/Model/Entity.php b/backend/lib/Model/Entity.php index eee07be..838e588 100644 --- a/backend/lib/Model/Entity.php +++ b/backend/lib/Model/Entity.php @@ -27,7 +27,7 @@ use Doctrine\Common\Collections; use Volkszaehler\Util; /** - * Entity superclass for all models with database persistance + * Entity superclass for all objects referenced by a UUID * * @author Steffen Vogel * @package default @@ -35,8 +35,12 @@ use Volkszaehler\Util; * @Entity * @Table(name="entities") * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="type", type="string") - * @DiscriminatorMap({"channel" = "Channel", "group" = "Group"}) + * @DiscriminatorColumn(name="class", type="string") + * @DiscriminatorMap({ + * "channel" = "Channel", + * "group" = "Aggregator" + * }) + * @HasLifecycleCallbacks */ abstract class Entity { /** @@ -49,6 +53,9 @@ abstract class Entity { /** @Column(type="string", length=36, nullable=false, unique=true) */ protected $uuid; + /** @Column(type="string", nullable=false) */ + protected $type; + /** * @OneToMany(targetEntity="Token", mappedBy="entity") */ @@ -63,75 +70,122 @@ abstract class Entity { /** * Constructor * + * @param string $type * @param array $properties of Model\Property */ - public function __construct($properties = array()) { + public function __construct($type, $properties = NULL) { + if (!EntityDefinition::exists($type)) { + throw new \Exception('unknown entity type'); + } + + $this->type = $type; $this->uuid = Util\UUID::mint(); + $this->tokens = new Collections\ArrayCollection(); $this->properties = new Collections\ArrayCollection(); + + if (isset($properties)) { + foreach($properties as $property) { + $this->properies->add($property); + } + } } /** - * Getter & setter + * Checks for optional and required properties according to share/entities.json + * + * Throws an exception if something is incorrect + * + * @PrePersist + * @PreUpdate + * @PostLoad + * @todo to be implemented */ + protected function validate() { + + } /** + * Get a property by name * * @param string $name * @return Model\Property */ public function getProperty($name) { - + return $this->properties->filter(function($property) use ($name) { + return $property->getName() == $name; + })->first(); } - public function getProperties() { - return $this->properties; + /** + * Get all properties or properties by prefix + * + * @param string $prefix + */ + public function getProperties($prefix = NULL) { + if (is_null($prefix)) { + return $this->properties; + } + else { + return $this->properties->filter(function($property) use ($prefix) { + return substr($property->getName(), 0, strlen($prefix) + 1) == $prefix . ':'; + }); + } } + /** + * @param string $name of the property + * @param string|integer|float $value of the property + * @todo to be implemented + */ public function setProperty($name, $value) { } + /** + * @param string $name of the property + * @todo to be implemented + */ public function unsetProperty($name) { } + /* + * Setter & Getter + */ public function getId() { return $this->id; } // read only public function getUuid() { return $this->uuid; } // read only + public function getType() { return $this->type; } // read only + public function getDefinition() { return EntityDefinition::get($this->type); } + + /** + * Get interpreter to obtain data and statistical information for a given time interval + * + * @param Doctrine\ORM\EntityManager $em + * @param integer $from timestamp in ms since 1970 + * @param integer $to timestamp in ms since 1970 + * @return Interpreter + */ + public function getInterpreter(\Doctrine\ORM\EntityManager $em, $from, $to) { + $interpreterClassName = 'Volkszaehler\Interpreter\\' . $this->getDefinition()->getInterpreter(); + return new $interpreterClassName($this, $em, $from, $to); + } } -class EntityDefiniton extends Util\JSONDefinition { - /** - * File containing the JSON definitons - * - * @var string - */ +class EntityDefinition extends Util\Definition { + /** @var string File containing the JSON definitons */ const FILE = '/share/entities.json'; - /** - * List of required properties - * Allowed properties = optional + required - * @var array - */ + /** @var array list of required properties */ protected $required = array(); - /** - * List of optional properties - * Allowed properties = optional + required - * @var array - */ + /** @var array list of optional properties */ protected $optional = array(); - /** - * Classname of intepreter (see backend/lib/Interpreter/) - * @var string - */ + /** @var string classname of intepreter (see backend/lib/Interpreter/) */ protected $interpreter; - /** - * Not required for group entity - * @var string - */ + /** @var string optional for Aggregator class entities */ protected $unit; /** @@ -140,15 +194,11 @@ class EntityDefiniton extends Util\JSONDefinition { */ protected $icon; - /** - * Check for required and optional properties - * - * @return boolean + /* + * Setter & Getter */ - public function checkProperties() { - - } - + public function getInterpreter() { return $this->interpreter; } + public function getUnit() { return $this->unit; } } ?> diff --git a/backend/lib/Model/Property.php b/backend/lib/Model/Property.php index 8df15d6..f3842d0 100644 --- a/backend/lib/Model/Property.php +++ b/backend/lib/Model/Property.php @@ -33,7 +33,13 @@ use Volkszaehler\Model; * @package default * * @Entity - * @Table(name="properties") + * @Table( + * name="properties", + * uniqueConstraints={ + * @UniqueConstraint(name="unique_properties", columns={"id", "name"}) + * } + * ) + * @HasLifecycleCallbacks */ class Property { /** @@ -54,15 +60,6 @@ class Property { /** @ManyToOne(targetEntity="Entity", inversedBy="properties") */ protected $entity; - /** - * Property definition - * - * Used to validate - * - * @var Model\PropertyDefinition - */ - protected $definition; - /** * Constructor * @@ -70,18 +67,30 @@ class Property { * @param string $value */ public function __construct($name, $value) { - $this->definition = Model\PropertyDefinition::get($name); - $this->setName($name); $this->setValue($value); } + /** + * Validate property name & value + * + * Throws an exception if something is incorrect + * + * @PrePersist + * @PreUpdate + * @PostLoad + * @todo to be implemented + */ + function validate() { + + } + /* * Setter & Getter */ - public function getName() { return $this->name; } public function getValue() { return $this->value; } + public function getDefinition() { return PropertyDefinition::get($name); } public function setValue($value) { if (!$this->definition->validate($value)) { @@ -97,80 +106,4 @@ class Property { protected function setName($name) { $this->name = $name; } } -class PropertyDefinition extends Util\JSONDefinition { - /** One of: string, numeric, multiple */ - public $type; - - /** - * Regex pattern to match if type == string - * - * @var string - */ - protected $pattern; - - /** - * Minimal value if type == numeric - * Required string length if type == string - * - * @var integer|float - */ - protected $min; - - /** - * Maximal value if type == numeric - * Allowed string length if type == string - * - * @var integer|float - */ - protected $max; - - /** - * List of possible choices if type == multiple - * (type as in javascript: 1.2 => numeric, "test" => string) - * - * @var array - */ - protected $choices = array(); - - - /** - * File containing the JSON definitons - * - * @var string - */ - const FILE = '/share/properties.json'; - - /** - * Validate value according to $this->type - * - * @param mixed $value - * @return boolean - */ - public function validate($value) { - switch ($this->type) { - case 'string': - $invalid = !is_string($value); - $invalid |= isset($this->pattern) && !preg_match($this->pattern, $value); - $invalid |= isset($this->min) && strlen($value) < $this->min; - $invalid |= isset($this->max) && strlen($value) > $this->max; - break; - - case 'numeric': - $invalid = !is_numeric($value); - $invalid |= isset($this->min) && $value < $this->min; - $invalid |= isset($this->max) && $value > $this->max; - break; - - case 'multiple': - $invalid = !in_array($value, $this->choices, TRUE); - break; - - default: - throw new \Exception('unknown property type'); - } - - return !$invalid; - } -} - ?> diff --git a/backend/lib/Model/PropertyDefinition.php b/backend/lib/Model/PropertyDefinition.php new file mode 100644 index 0000000..5126253 --- /dev/null +++ b/backend/lib/Model/PropertyDefinition.php @@ -0,0 +1,107 @@ +. + */ + +namespace Volkszaehler\Model; + +use Volkszaehler\Util; + +class PropertyDefinition extends Util\Definition { + /** One of: string, numeric, multiple */ + public $type; + + /** @var string regex pattern to match if type == string */ + protected $pattern; + + /** + * Minimal value if type == integer or type == float + * Required string length if type == string + * + * @var integer|float + */ + protected $min; + + /** + * Maximal value if type == integer or type == float + * Allowed string length if type == string + * + * @var integer|float + */ + protected $max; + + /** + * List of possible choices if type == multiple + * (type as in javascript: 1.2 => float, 5 => integer, "test" => string) + * + * @var array + */ + protected $choices = array(); + + + /** + * File containing the JSON definitons + * + * @var string + */ + const FILE = '/share/properties.json'; + + /** + * Validate value according to $this->type + * + * @param string|numeric $value + * @return boolean + */ + public function validateValue($value) { + switch ($this->type) { + case 'string': + $invalid = !is_string($value); + $invalid |= isset($this->pattern) && !preg_match($this->pattern, $value); + $invalid |= isset($this->min) && strlen($value) < $this->min; + $invalid |= isset($this->max) && strlen($value) > $this->max; + break; + + case 'integer': + $invalid = !is_int($value); + break; + + case 'float': + $invalid = !is_float($value); + break; + + case 'multiple': + $invalid = !in_array($value, $this->choices, TRUE); + break; + + default: + throw new \Exception('unknown property type'); + } + + if ($type == 'integer' || $type == 'float') { + $invalid |= isset($this->min) && $value < $this->min; + $invalid |= isset($this->max) && $value > $this->max; + } + + return !$invalid; + } +} + +?> diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelGroupProxy.php b/backend/lib/Model/Proxy/VolkszaehlerModelAggregatorProxy.php similarity index 61% rename from backend/lib/Model/Proxies/VolkszaehlerModelGroupProxy.php rename to backend/lib/Model/Proxy/VolkszaehlerModelAggregatorProxy.php index feee410..a75d5c8 100644 --- a/backend/lib/Model/Proxies/VolkszaehlerModelGroupProxy.php +++ b/backend/lib/Model/Proxy/VolkszaehlerModelAggregatorProxy.php @@ -1,11 +1,11 @@ _load(); - return parent::addGroup($child); + return parent::addAggregator($child); } - public function removeGroup(\Volkszaehler\Model\Group $child) + public function removeAggregator(\Volkszaehler\Model\Aggregator $child) { $this->_load(); - return parent::removeGroup($child); + return parent::removeAggregator($child); } public function addChannel(\Volkszaehler\Model\Channel $child) @@ -51,54 +51,6 @@ class VolkszaehlerModelGroupProxy extends \Volkszaehler\Model\Group implements \ return parent::removeChannel($child); } - public function getName() - { - $this->_load(); - return parent::getName(); - } - - public function setName($name) - { - $this->_load(); - return parent::setName($name); - } - - public function getDescription() - { - $this->_load(); - return parent::getDescription(); - } - - public function setDescription($description) - { - $this->_load(); - return parent::setDescription($description); - } - - public function getChildren() - { - $this->_load(); - return parent::getChildren(); - } - - public function getParents() - { - $this->_load(); - return parent::getParents(); - } - - public function getChannels() - { - $this->_load(); - return parent::getChannels(); - } - - public function getInterpreter(\Doctrine\ORM\EntityManager $em) - { - $this->_load(); - return parent::getInterpreter($em); - } - public function getProperty($name) { $this->_load(); @@ -135,9 +87,21 @@ class VolkszaehlerModelGroupProxy extends \Volkszaehler\Model\Group implements \ return parent::getUuid(); } + public function getDefinition() + { + $this->_load(); + return parent::getDefinition(); + } + + public function getInterpreter(\Doctrine\ORM\EntityManager $em, $from, $to) + { + $this->_load(); + return parent::getInterpreter($em, $from, $to); + } + public function __sleep() { - return array('__isInitialized__', 'id', 'uuid', 'tokens', 'properties', 'channels', 'children', 'parents'); + return array('__isInitialized__', 'id', 'uuid', 'type', 'tokens', 'properties', 'channels', 'children', 'parents'); } } \ No newline at end of file diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelChannelProxy.php b/backend/lib/Model/Proxy/VolkszaehlerModelChannelProxy.php similarity index 65% rename from backend/lib/Model/Proxies/VolkszaehlerModelChannelProxy.php rename to backend/lib/Model/Proxy/VolkszaehlerModelChannelProxy.php index a573b88..2d72789 100644 --- a/backend/lib/Model/Proxies/VolkszaehlerModelChannelProxy.php +++ b/backend/lib/Model/Proxy/VolkszaehlerModelChannelProxy.php @@ -1,6 +1,6 @@ _load(); - return parent::getInterpreter($em, $from, $to); - } - - public function getName() - { - $this->_load(); - return parent::getName(); - } - - public function setName($name) - { - $this->_load(); - return parent::setName($name); - } - - public function getDescription() - { - $this->_load(); - return parent::getDescription(); - } - - public function setDescription($description) - { - $this->_load(); - return parent::setDescription($description); - } - - public function getResolution() - { - $this->_load(); - return parent::getResolution(); - } - - public function setResolution($resolution) - { - $this->_load(); - return parent::setResolution($resolution); - } - - public function getCost() - { - $this->_load(); - return parent::getCost(); - } - - public function setCost($cost) - { - $this->_load(); - return parent::setCost($cost); - } - public function getProperty($name) { $this->_load(); @@ -123,9 +69,21 @@ class VolkszaehlerModelChannelProxy extends \Volkszaehler\Model\Channel implemen return parent::getUuid(); } + public function getDefinition() + { + $this->_load(); + return parent::getDefinition(); + } + + public function getInterpreter(\Doctrine\ORM\EntityManager $em, $from, $to) + { + $this->_load(); + return parent::getInterpreter($em, $from, $to); + } + public function __sleep() { - return array('__isInitialized__', 'id', 'uuid', 'tokens', 'properties', 'data', 'groups'); + return array('__isInitialized__', 'id', 'uuid', 'type', 'tokens', 'properties', 'data', 'aggregators'); } } \ No newline at end of file diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelDataProxy.php b/backend/lib/Model/Proxy/VolkszaehlerModelDataProxy.php similarity index 97% rename from backend/lib/Model/Proxies/VolkszaehlerModelDataProxy.php rename to backend/lib/Model/Proxy/VolkszaehlerModelDataProxy.php index db04e1e..93214f2 100644 --- a/backend/lib/Model/Proxies/VolkszaehlerModelDataProxy.php +++ b/backend/lib/Model/Proxy/VolkszaehlerModelDataProxy.php @@ -1,6 +1,6 @@ _load(); + return parent::getDefinition(); + } + + public function getInterpreter(\Doctrine\ORM\EntityManager $em, $from, $to) + { + $this->_load(); + return parent::getInterpreter($em, $from, $to); + } + public function __sleep() { - return array('__isInitialized__', 'id', 'uuid', 'tokens', 'properties'); + return array('__isInitialized__', 'id', 'uuid', 'type', 'tokens', 'properties'); } } \ No newline at end of file diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelPropertyProxy.php b/backend/lib/Model/Proxy/VolkszaehlerModelPropertyProxy.php similarity index 83% rename from backend/lib/Model/Proxies/VolkszaehlerModelPropertyProxy.php rename to backend/lib/Model/Proxy/VolkszaehlerModelPropertyProxy.php index 42860e1..0df9620 100644 --- a/backend/lib/Model/Proxies/VolkszaehlerModelPropertyProxy.php +++ b/backend/lib/Model/Proxy/VolkszaehlerModelPropertyProxy.php @@ -1,6 +1,6 @@ _load(); + return parent::validate(); + } + public function getName() { $this->_load(); @@ -39,6 +45,12 @@ class VolkszaehlerModelPropertyProxy extends \Volkszaehler\Model\Property implem return parent::getValue(); } + public function getDefinition() + { + $this->_load(); + return parent::getDefinition(); + } + public function setValue($value) { $this->_load(); diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelTokenProxy.php b/backend/lib/Model/Proxy/VolkszaehlerModelTokenProxy.php similarity index 96% rename from backend/lib/Model/Proxies/VolkszaehlerModelTokenProxy.php rename to backend/lib/Model/Proxy/VolkszaehlerModelTokenProxy.php index aca599b..16f7d36 100644 --- a/backend/lib/Model/Proxies/VolkszaehlerModelTokenProxy.php +++ b/backend/lib/Model/Proxy/VolkszaehlerModelTokenProxy.php @@ -1,6 +1,6 @@ csv = array_merge($this->csv, $data); } - public function addGroup(Model\Group $group) { + public function addAggregator(Model\Aggregator $aggregator) { } diff --git a/backend/lib/View/HTTP/Request.php b/backend/lib/View/HTTP/Request.php index 695c59e..9820140 100644 --- a/backend/lib/View/HTTP/Request.php +++ b/backend/lib/View/HTTP/Request.php @@ -65,9 +65,9 @@ class Request { */ public function getHeader($header) { return $this->headers[$header]; } public function getMethod() { return $this->method; } - public function getParameter($name, $method = 'get') { - return (isset($this->parameters[$method][$name])) ? $this->parameters[$method][$name] : NULL; - } + public function getParameter($name, $method = 'get') { return (isset($this->parameters[$method][$name])) ? $this->parameters[$method][$name] : NULL; } + public function getParameters($method = 'get') { return $this->parameters[$method]; } + } ?> diff --git a/backend/lib/View/JSON.php b/backend/lib/View/JSON.php index d8b5c10..2b77560 100644 --- a/backend/lib/View/JSON.php +++ b/backend/lib/View/JSON.php @@ -71,8 +71,8 @@ class JSON extends View { $this->json['channels'][] = $jsonChannel; } - public function addGroup(Model\Group $group, $recursive = FALSE) { - $this->json['groups'][] = self::convertJson($group, $recursive); + public function addAggregator(Model\Aggregator $aggregator, $recursive = FALSE) { + $this->json['groups'][] = self::convertJson($aggregator, $recursive); } public function addDebug(Util\Debug $debug) { @@ -97,27 +97,27 @@ class JSON extends View { ); } - protected static function convertGroup(Model\Group $group, $recursive = FALSE) { - $jsonGroup = array(); + protected static function convertAggregator(Model\Aggregator $aggregator, $recursive = FALSE) { + $jsonAggregator = array(); - $jsonGroup['uuid'] = (string) $group->getUuid(); - $jsonGroup['name'] = $group->getName(); - $jsonGroup['description'] = $group->getDescription(); - $jsonGroup['channels'] = array(); + $jsonAggregator['uuid'] = (string) $aggregator->getUuid(); + $jsonAggregator['name'] = $aggregator->getName(); + $jsonAggregator['description'] = $aggregator->getDescription(); + $jsonAggregator['channels'] = array(); - foreach ($group->getChannels() as $channel) { - $jsonGroup['channels'][] = (string) $channel->getUuid(); + foreach ($aggregator->getChannels() as $channel) { + $jsonAggregator['channels'][] = (string) $channel->getUuid(); } if ($recursive) { - $jsonGroup['children'] = array(); + $jsonAggregator['children'] = array(); - foreach ($group->getChildren() as $subGroup) { - $jsonGroup['children'][] = $this->toJson($subGroup, $recursive); // recursion + foreach ($aggregator->getChildren() as $subAggregator) { + $jsonAggregator['children'][] = $this->toJson($subAggregator, $recursive); // recursion } } - return $jsonGroup; + return $jsonAggregator; } protected static function convertData($data) { diff --git a/backend/lib/View/JpGraph.php b/backend/lib/View/JpGraph.php index 99a228f..51cfda5 100644 --- a/backend/lib/View/JpGraph.php +++ b/backend/lib/View/JpGraph.php @@ -26,9 +26,9 @@ namespace Volkszaehler\View; use Volkszaehler\Model; use Volkszaehler\Util; -require_once \Volkszaehler\BACKEND_DIR . '/lib/vendor/JpGraph/jpgraph.php'; -require_once \Volkszaehler\BACKEND_DIR . '/lib/vendor/JpGraph/jpgraph_scatter.php'; -require_once \Volkszaehler\BACKEND_DIR . '/lib/vendor/JpGraph/jpgraph_date.php'; +require_once VZ_BACKEND_DIR . '/lib/vendor/JpGraph/jpgraph.php'; +require_once VZ_BACKEND_DIR . '/lib/vendor/JpGraph/jpgraph_scatter.php'; +require_once VZ_BACKEND_DIR . '/lib/vendor/JpGraph/jpgraph_date.php'; /** * Plotting and graphing of data on the server side @@ -106,7 +106,7 @@ class JpGraph extends View { // Create the scatter plot $plot = new \ScatterPlot($yData, $xData); - $plot->setLegend($channel->getName() . ': ' . $channel->getDescription() . ' [' . $channel->getUnit() . ']'); + $plot->setLegend($channel->getProperty('name')->getValue() . ': ' . $channel->getProperty('description')->getValue() . ' [' . $channel->getDefinition()->getUnit() . ']'); $plot->SetLinkPoints(TRUE, self::$colors[$count]); $plot->mark->SetColor(self::$colors[$count]); @@ -132,10 +132,10 @@ class JpGraph extends View { /** * adds all channel of group as new plots to the graph * - * @param Model\Group $group + * @param Model\Aggregator $aggregator */ - public function addGroup(Model\Group $group) { - foreach ($group->getChannels() as $child) { + public function addAggregator(Model\Aggregator $aggregator) { + foreach ($aggregator->getChannels() as $child) { $this->addChannel($child); } } @@ -157,24 +157,25 @@ class JpGraph extends View { * check weather a axis for the indicator of $channel exists * * @param \Volkszaehler\Model\Channel $channel + * @todo call getType() only once */ protected function getAxisIndex(\Volkszaehler\Model\Channel $channel) { - if (!in_array($channel->getIndicator(), array_keys($this->axes))) { + if (!in_array($channel->getType(), array_keys($this->axes))) { $count =count($this->axes); if ($count == 0) { - $this->axes[$channel->getIndicator()] = -1; + $this->axes[$channel->getType()] = -1; $yaxis = $this->graph->yaxis; } else { - $this->axes[$channel->getIndicator()] = $count - 1; + $this->axes[$channel->getType()] = $count - 1; - $this->graph->SetYScale($this->axes[$channel->getIndicator()],'lin'); + $this->graph->SetYScale($this->axes[$channel->getType()],'lin'); - $yaxis = $this->graph->ynaxis[$this->axes[$channel->getIndicator()]]; + $yaxis = $this->graph->ynaxis[$this->axes[$channel->getType()]]; } - $yaxis->title->Set($channel->getUnit()); + $yaxis->title->Set($channel->getDefinition()->getUnit()); $yaxis->SetFont(FF_ARIAL); $yaxis->title->SetFont(FF_ARIAL); @@ -182,7 +183,7 @@ class JpGraph extends View { $yaxis->SetTitleMargin('50'); } - return $this->axes[$channel->getIndicator()]; + return $this->axes[$channel->getType()]; } /** diff --git a/backend/lib/View/PlainText.php b/backend/lib/View/PlainText.php index 0656fc8..a03b754 100644 --- a/backend/lib/View/PlainText.php +++ b/backend/lib/View/PlainText.php @@ -51,8 +51,8 @@ class PlainText extends View { var_dump($data); } - public function addGroup(Model\Group $group) { - var_dump($group); + public function addAggregator(Model\Aggregator $aggregator) { + var_dump($aggregator); } public function addDebug(Util\Debug $debug) { diff --git a/backend/lib/View/View.php b/backend/lib/View/View.php index 383a2e6..21e6da4 100644 --- a/backend/lib/View/View.php +++ b/backend/lib/View/View.php @@ -35,7 +35,7 @@ use Volkszaehler\Util; */ interface ViewInterface { public function addChannel(Model\Channel $channel, array $data = NULL); - function addGroup(Model\Group $group); + function addAggregator(Model\Aggregator $aggregator); function addDebug(Util\Debug $debug); } @@ -73,13 +73,16 @@ abstract class View implements ViewInterface { $code = ($exception->getCode() == 0 && HTTP\Response::getCodeDescription($exception->getCode())) ? 400 : $exception->getCode(); $this->response->setCode($code); + $this->sendResponse(); - echo $exception; - //$this->sendResponse(); die(); } public function sendResponse() { + if (Util\Debug::isActivated()) { + $this->addDebug(Util\Debug::getInstance()); + } + $this->renderResponse(); $this->response->send(); } diff --git a/backend/lib/View/XML.php b/backend/lib/View/XML.php index 35147d1..e0638fe 100644 --- a/backend/lib/View/XML.php +++ b/backend/lib/View/XML.php @@ -37,7 +37,7 @@ class XML extends View { protected $xmlDoc = NULL; protected $xmlRoot = NULL; protected $xmlChannels = NULL; - protected $xmlGroups = NULL; + protected $xmlAggregators = NULL; public function __construct(HTTP\Request $request, HTTP\Response $response) { parent::__construct($request, $response); @@ -88,32 +88,32 @@ class XML extends View { $this->xmlChannels->appendChild($xmlChannel); } - public function addGroup(Model\Group $group, $recursive = FALSE) { - if (!isset($this->xmlGroups)) { - $this->xmlGroups = $this->xmlDoc->createElement('groups'); - $this->xmlRoot->appendChild($this->xmlGroups); + public function addAggregator(Model\Aggregator $aggregator, $recursive = FALSE) { + if (!isset($this->xmlAggregators)) { + $this->xmlAggregators = $this->xmlDoc->createElement('groups'); + $this->xmlRoot->appendChild($this->xmlAggregators); } - $this->xmlGroups->appendChild($this->toXml($group, $recursive)); + $this->xmlAggregators->appendChild($this->toXml($aggregator, $recursive)); } - public function toXml(Model\Group $group, $recursive = FALSE) { - $xmlGroup = $this->xmlDoc->createElement('group'); - $xmlGroup->setAttribute('uuid', $group->getUuid()); - $xmlGroup->appendChild($this->xmlDoc->createElement('name', $group->getName())); - $xmlGroup->appendChild($this->xmlDoc->createElement('description', $group->getDescription())); + public function toXml(Model\Aggregator $aggregator, $recursive = FALSE) { + $xmlAggregator = $this->xmlDoc->createElement('group'); + $xmlAggregator->setAttribute('uuid', $aggregator->getUuid()); + $xmlAggregator->appendChild($this->xmlDoc->createElement('name', $aggregator->getName())); + $xmlAggregator->appendChild($this->xmlDoc->createElement('description', $aggregator->getDescription())); if ($recursive) { $xmlChildren = $this->xmlDoc->createElement('children'); - foreach ($group->getChildren() as $child) { + foreach ($aggregator->getChildren() as $child) { $xmlChildren->appendChild($this->toXml($child, $recursive)); } - $xmlGroup->appendChild($xmlChildren); + $xmlAggregator->appendChild($xmlChildren); } - return $xmlGroup; + return $xmlAggregator; } public function addDebug(Util\Debug $debug) { diff --git a/share/entities.json b/share/entities.json index 74864fc..91894e1 100644 --- a/share/entities.json +++ b/share/entities.json @@ -28,14 +28,21 @@ [ { "name" : "group", - "required" : ["name"], + "required" : ["title"], "optional" : ["description", "details:", "owner:", "tolerance"], - "interpreter" : "GroupInterpreter", + "interpreter" : "AggregatorInterpreter", + "icon" : "" // TODO look for an icon + }, + { + "name" : "user", + "required" : ["title"], + "optional" : ["description", "details:", "owner:", "tolerance"], + "interpreter" : "AggregatorInterpreter", "icon" : "" // TODO look for an icon }, { "name" : "power", - "required" : ["name", "resolution"], + "required" : ["title", "resolution"], "optional" : ["description", "details:", "owner:", "address:", "tolerance"], "interpreter" : "MeterInterpreter", "unit" : "kW/h", @@ -43,7 +50,7 @@ }, { "name" : "gas", - "required" : ["name", "resolution"], + "required" : ["title", "resolution"], "optional" : ["description", "details:", "owner:", "address:", "tolerance"], "interpreter" : "MeterInterpreter", "unit" : "m³/h", @@ -51,7 +58,7 @@ }, { "name" : "water", - "required" : ["name", "resolution"], + "required" : ["title", "resolution"], "optional" : ["description", "details:", "owner:", "address:", "tolerance"], "interpreter" : "MeterInterpreter", "unit" : "m³/h", @@ -59,7 +66,7 @@ }, { "name" : "temperature", - "required" : ["name"], + "required" : ["title"], "optional" : ["description", "details:", "owner:", "address:", "tolerance"], "interpreter" : "SensorInterpreter", "unit" : "°C", @@ -67,7 +74,7 @@ }, { "name" : "pressure", - "required" : ["name"], + "required" : ["title"], "optional" : ["description", "details:", "owner:", "address:", "tolerance"], "interpreter" : "SensorInterpreter", "unit" : "hPa", @@ -75,7 +82,7 @@ }, { "name" : "humidity", - "required" : ["name"], + "required" : ["title"], "optional" : ["description", "details:", "owner:", "address:", "tolerance"], "interpreter" : "SensorInterpreter", "unit" : "%", @@ -83,7 +90,7 @@ }, { "name" : "windspeed", - "required" : ["name"], + "required" : ["title"], "optional" : ["description", "details:", "owner:", "address:", "tolerance"], "interpreter" : "SensorInterpreter", "unit" : "km/h", @@ -91,16 +98,16 @@ }, { "name" : "radition", - "required" : ["name"], + "required" : ["title"], "optional" : ["description", "details:", "owner:", "address:", "tolerance"], "interpreter" : "SensorInterpreter", "icon" : "" // TODO look for an icon }, { "name" : "luminosity", - "required" : ["name"], + "required" : ["title"], "optional" : ["description", "details:", "owner:", "address:", "tolerance"], "interpreter" : "SensorInterpreter", "icon" : "" // TODO look for an icon - }, + } ] diff --git a/share/properties.json b/share/properties.json index ead82fc..3992ca2 100644 --- a/share/properties.json +++ b/share/properties.json @@ -27,7 +27,7 @@ [ { - "name" : "name", + "name" : "title", "type" : "string", "pattern" : "/[a-z0-9 ]/", "min": 3, @@ -41,29 +41,29 @@ }, { "name" : "cost", - "type" : "numeric", + "type" : "float", "min" : 0 }, { "name" : "resolution", - "type" : "numeric", + "type" : "integer", "min" : 1 }, { "name" : "tolerance", - "type" : "numeric", + "type" : "float", "min" : 0, "max" : 1 }, { "name" : "address:lat", - "type" : "numeric", + "type" : "float", "min" : -90, "max" : 90 }, { "name" : "address:lon", - "type" : "numeric", + "type" : "float", "min" : -90, "max" : 90 }, @@ -82,7 +82,198 @@ }, { "name" : "address:state", - "type" : "string" + "type" : "multiple", + "choices" : [ + "Albania", + "Algeria", + "Andorra", + "Angola", + "Anguilla", + "Antigua and Barbuda", + "Argentina", + "Armenia", + "Aruba", + "Australia", + "Austria", + "Azerbaijan Republic", + "Bahamas", + "Bahrain", + "Barbados", + "Belgium", + "Belize", + "Benin", + "Bermuda", + "Bhutan", + "Bolivia", + "Bosnia and Herzegovina", + "Botswana", + "Brazil", + "British Virgin Islands", + "Brunei", + "Bulgaria", + "Burkina Faso", + "Burundi", + "Cambodia", + "Canada", + "Cape Verde", + "Cayman Islands", + "Chad", + "Chile", + "China Worldwide", + "Colombia", + "Comoros", + "Cook Islands", + "Costa Rica", + "Croatia", + "Cyprus", + "Czech Republic", + "Democratic Republic of the Congo", + "Denmark", + "Djibouti", + "Dominica", + "Dominican Republic", + "Ecuador", + "El Salvador", + "Eritrea", + "Estonia", + "Ethiopia", + "Falkland Islands", + "Faroe Islands", + "Federated States of Micronesia", + "Fiji", + "Finland", + "France", + "French Guiana", + "French Polynesia", + "Gabon Republic", + "Gambia", + "Germany", + "Gibraltar", + "Greece", + "Greenland", + "Grenada", + "Guadeloupe", + "Guatemala", + "Guinea", + "Guinea Bissau", + "Guyana", + "Honduras", + "Hong Kong", + "Hungary", + "Iceland", + "India", + "Indonesia", + "Ireland", + "Israel", + "Italy", + "Jamaica", + "Japan", + "Jordan", + "Kazakhstan", + "Kenya", + "Kiribati", + "Kuwait", + "Kyrgyzstan", + "Laos", + "Latvia", + "Lesotho", + "Liechtenstein", + "Lithuania", + "Luxembourg", + "Madagascar", + "Malawi", + "Malaysia", + "Maldives", + "Mali", + "Malta", + "Marshall Islands", + "Martinique", + "Mauritania", + "Mauritius", + "Mayotte", + "Mexico", + "Mongolia", + "Montserrat", + "Morocco", + "Mozambique", + "Namibia", + "Nauru", + "Nepal", + "Netherlands", + "Netherlands Antilles", + "New Caledonia", + "New Zealand", + "Nicaragua", + "Niger", + "Niue", + "Norfolk Island", + "Norway", + "Oman", + "Palau", + "Panama", + "Papua New Guinea", + "Peru", + "Philippines", + "Pitcairn Islands", + "Poland", + "Portugal", + "Qatar", + "Republic of the Congo", + "Reunion", + "Romania", + "Russia", + "Rwanda", + "Saint Vincent and the Grenadines", + "Samoa", + "San Marino", + "São Tomé and Príncipe", + "Saudi Arabia", + "Senegal", + "Seychelles", + "Sierra Leone", + "Singapore", + "Slovakia", + "Slovenia", + "Solomon Islands", + "Somalia", + "South Africa", + "South Korea", + "Spain", + "Sri Lanka", + "St. Helena", + "St. Kitts and Nevis", + "St. Lucia", + "St. Pierre and Miquelon", + "Suriname", + "Svalbard and Jan Mayen Islands", + "Swaziland", + "Sweden", + "Switzerland", + "Taiwan", + "Tajikistan", + "Tanzania", + "Thailand", + "Togo", + "Tonga", + "Trinidad and Tobago", + "Tunisia", + "Turkey", + "Turkmenistan", + "Turks and Caicos Islands", + "Tuvalu", + "Uganda", + "Ukraine", + "United Arab Emirates", + "United Kingdom", + "Uruguay", + "Vanuatu", + "Vatican City State", + "Venezuela", + "Vietnam", + "Wallis and Futuna Islands", + "Yemen", + "Zambia" + ] }, { "name" : "address:country",