Is there a python library for converting a JSON schema to a python class definition, similar to jsonschema2pojo -- https://github.com/joelittlejohn/jsonschema2pojo -- for Java?
So far the closest thing I've been able to find is warlock, which advertises this workflow:
Build your schema
>>> schema = {
'name': 'Country',
'properties': {
'name': {'type': 'string'},
'abbreviation': {'type': 'string'},
},
'additionalProperties': False,
}
Create a model
>>> import warlock
>>> Country = warlock.model_factory(schema)
Create an object using your model
>>> sweden = Country(name='Sweden', abbreviation='SE')
However, it's not quite that easy. The objects that Warlock produces lack much in the way of introspectible goodies. And if it supports nested dicts at initialization, I was unable to figure out how to make them work.
To give a little background, the problem that I was working on was how to take Chrome's JSONSchema API and produce a tree of request generators and response handlers. Warlock doesn't seem too far off the mark, the only downside is that meta-classes in Python can't really be turned into 'code'.
Other useful modules to look for:
jsonschema - (which Warlock is built on top of)
valideer - similar to jsonschema but with a worse name.
bunch - An interesting structure builder thats half-way between a dotdict and construct
If you end up finding a good one-stop solution for this please follow up your question - I'd love to find one. I poured through github, pypi, googlecode, sourceforge, etc.. And just couldn't find anything really sexy.
For lack of any pre-made solutions, I'll probably cobble together something with Warlock myself. So if I beat you to it, I'll update my answer. :p
python-jsonschema-objects is an alternative to warlock, build on top of jsonschema
python-jsonschema-objects provides an automatic class-based binding to JSON schemas for use in python.
Usage:
Sample Json Schema
schema = '''{
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
},
"dogs": {
"type": "array",
"items": {"type": "string"},
"maxItems": 4
},
"gender": {
"type": "string",
"enum": ["male", "female"]
},
"deceased": {
"enum": ["yes", "no", 1, 0, "true", "false"]
}
},
"required": ["firstName", "lastName"]
} '''
Converting the schema object to class
import python_jsonschema_objects as pjs
import json
schema = json.loads(schema)
builder = pjs.ObjectBuilder(schema)
ns = builder.build_classes()
Person = ns.ExampleSchema
james = Person(firstName="James", lastName="Bond")
james.lastName
u'Bond' james
example_schema lastName=Bond age=None firstName=James
Validation :
james.age = -2
python_jsonschema_objects.validators.ValidationError: -2 was less
or equal to than 0
But problem is , it is still using draft4validation while jsonschema has moved over draft4validation , i filed an issue on the repo regarding this .
Unless you are using old version of jsonschema , the above package will work as shown.
I just created this small project to generate code classes from json schema, even if dealing with python I think can be useful when working in business projects:
pip install jsonschema2popo
running following command will generate a python module containing json-schema defined classes (it uses jinja2 templating)
jsonschema2popo -o /path/to/output_file.py /path/to/json_schema.json
more info at: https://github.com/frx08/jsonschema2popo
Related
I'm not sure how to assign a slot to a variable in an Alexa skill. I've found several tutorials, but most of them are in JS (I wrote this code in Python) or outdated, since even using them precisely as presented does not work.
Alexa is meant to ask for one of my kids' names so I can implement a personalized answer, but I can't find a way to make use of the name once she gets it.
Here's how I call it in my code (look at variable "kids_name" in particular):
#sb.request_handler(can_handle_func=lambda input:
currently_playing(input) and
is_intent_name("ChoresIntent")(input))
def chores_intent_handler(handler_input):
session_attr = handler_input.attributes_manager.session_attributes
original_date = date(2022, 4, 8)
today = date.today()
diff = (today - original_date).days
kids_name = handler_input.request_envelope.request.intent.slots.name.value
mod = "Error. No input."
That seemed to be how they set the variable in the most recent tutorial I found, but it absolutely will not run for me. I've watched tutorials on pulling data from JSON files, but none of their answers look anything like this.
As I understand it, I construct the path from JSON, but I don't understand the syntax. Here's the JSON for my skill. I would really appreciate some clarification on how to transfer handler answers from one to the other. While I think I get the basic structure of the dictionaries, the methods I see for accessing them is very confusing for me.
{
"name": "ChoresIntent",
"slots": [
{
"name": "childname",
"type": "childname"
}
],
"samples": [
"what are {childname} s jobs today",
"what are my jobs today",
"can you tell me {childname} s chores",
]
}
],
"types": [
{
"name": "childname",
"values": [
{
"name": {
"value": "Matthew",
"synonyms": [
"Mattie"
]
}
},
Thank you in advance! I really appreciate the help I get on here.
You have 2 options
Without ask_sdk:
kids_name = handler_input.request_envelope.request.intent.slots["name"].value
With ask_sdk:
kids_name = ask_utils.request_util.get_slot_value(handler_input, "name")
# You can also get the slot and then set the value:
slotName = ask_utils.request_util.get_slot(handler_input, "name")
kids_name = slotName.value
Is there any other option aside from wrapping itemgetter in a try/except block in case of a missing key?
Like dict.get('bar', 'foo')?
Example of usage:
currentUser = {
"id": 24,
"name": "John Doe",
"website": "http://mywebsite.com",
"description": "I am an actor",
"email": "example#example.com",
"phone_number": "+12345678",
"username": "johndoe",
"birth_date": "1991-02-23",
"followers": 46263,
"following": 345,
"like": 204,
"comments": 9
}
id, like, hhh = itemgetter("id","like", "hhh")(currentUser)
No, it is not possible. See the source code. As mentioned in the comment above, there is an open issue to change the behaviour. Unfortunately, you can't patch attributes like __init__ directly on a built-in/extension type. Moreover, operator.itemgetter is not an acceptable base type, so you can't write a new class to patch it either. The only option would be to write a completely new class from the ground up.
I'm using the French analyzer.
Having examined the output from IndexClient.analyze(...) for this analyzer I'm a little unhappy with some of the stopwords (e.g. the expression 'ayant-cause' comes out as 'caus', because 'ayant' is a stopword: French stopwords).
How do I go about examining these stopwords and then tweaking them? Do I have to create a custom analyzer based on the existing French one? Or can I directly tweak the French one?
NB I am using the Python elasticsearch module ("thin client"), but an answer in terms of REST commands would be fine.
Yes, you can easily tweak the existing analyzer and examine them using the Analyze API of elasticsearch
Ultimately analyzer is made of three things, char filter, tokeniser and token-filter and you can create your own combination of these things to build your own custom analyzer and test it using the REST API.
Spent quite a bit of time figuring out at least a workaround arrangement.
Having downloaded that French stop-words file from Github I then edited it (e.g. to exclude "ayant"). Currently residing in the "config" directory of my installed ES setup (although you can set an absolute path).
Then I made my settings/mappings object like this:
{
'settings' : {
'analysis' : {
'analyzer' : {
'tweaked_french' : {
'type' : 'french',
# NB W10, config path currently D:\apps\ElasticSearch\elasticsearch-7.10.2\config
'stopwords_path' : 'tweaked_french_stop.txt',
},
},
},
},
'mappings': {
'dynamic': 'strict',
'properties': {
'my_french_field' : {
'type' : 'text',
'term_vector' : 'with_positions_offsets',
'fields' : {
'french' : {
'type' : 'text',
'analyzer' : 'tweaked_french',
'term_vector' : 'with_positions_offsets',
},
},
},
},
},
}
What is then rather wonderful is that, according to my experiments, you can get a query object to find and use that custom-built analyser (i.e. it's there and available, in the installed index). So your query object is relatively simple:
{
'query': {
'simple_query_string': {
'query': query_text,
'fields': [
'my_french_field.french',
],
'analyzer' : 'tweaked_french',
},
},
'highlight': {
'fields': {
'my_french_field.french': {
'type': 'fvh',
...
},
},
'number_of_fragments': 0
}
}
After that you can query in French: your query gets stemmed and the result is used for the search. If "ayant" is a word in your query string, it will now return hits including "ayant-cause", proving that both the query and the mapping spec are using the tweaked stop-word list.
I'd still like to know whether a way exists not involving using an external file, i.e. of editing on-the-fly what is already there (or of just seeing what it already there...).
I have a .geojson file (call it data.geojson) which I use to manually update a dataset on mapbox.
Suppose that my data.geojson file is structured as follows:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"suburb": "A",
"unemployed": 10
},
"geometry": {
"type": "Point",
"coordinates": [
0,
0
]
}
},
{
"type": "Feature",
"properties": {
"suburb": "B",
"unemployed": 20
},
"geometry": {
"type": "Point",
"coordinates": [
1,
1
]
}
data.geojson is stored locally, and every 12 hours the 'unemployed' property of each feature is updated using another python script that scrapes data from the web.
Currently, in order to update these properties within the online dataset (stored at mapbox.com) I am manually navigating to the Mapbox website and reuploading the data.geojson file. I am looking for a way to accomplish this task pythonically.
Any help would be greatly appreciated!
you can setup a timer of some sort to automatically update the data using javascript functions. Here I am using a source and layer named "STI", which is just geoJSON line data.
The function would first add the source of the data as well as the layer :
var STI_SOURCE = 'json/sti/STI.json'; // declare URL for data
map.addSource('sti', { type: 'geojson', data: STI1 }); // Add source using URL
// Add the actual layer using the source
map.addLayer({
"id": "sti",
"type": "line",
"source": "sti",
"layout": {
"line-join": "miter",
"line-cap": "round"
},
"paint": {
"line-color": "#fff",
"line-width": 1,
"line-dasharray": [6, 2]
}
});
Then, when you want to refresh the data - remove them :
map.removeLayer('sti');
map.removeSource('sti');
Then, you can re-add them by starting at the beginning. There are other ways (and better) to do this, but this is just one way that works. I think there is a setData() function that does this better. But hopefully this can get you started.
My solution, in the end, was simply to point the source of the Mapbox layer to the locally stored dataset.geojson file rather than the corresponding dataset stored online at mapbox.com.
I was able to edit the locally stored dataset.geojson using the 'json' python package. Since the Mapbox layer source was pointing directly to the local dataset, all updates to this local file would then be reflected in the Mapbox layer. This way, there is no need to upload any data to Mapbox.
#David also posted a helpful solution if you wish to go down that route.
I have an existing python application (limited deployment) that requires the ability to run batches/macros (ie do foo 3 times, change x, do y). Currently I have this implemented as exec running through a text file which contains simple python code to do all the required batching.
However exec is messy (ie security issues) and there are also some cases where it doesn't act exactly the same as actually having the same code in your file. How can I get around using exec? I don't want to write my own mini-macro language, and users need to use multiple different macros per session, so I can't setup it such that the macro is a python file that calls the software and then runs itself or something similar.
Is there a cleaner/better way to do this?
Pseudocode: In the software it has something like:
-when a macro gets called
for line in macrofile:
exec line
and the macrofiles are python, ie something like:
property_of_software_obj = "some str"
software_function(some args)
etc.
Have you considered using a serialized data format like JSON? It's lightweight, can easily translate to Python dictionaries, and all the cool kids are using it.
You could construct the data in a way that is meaningful, but doesn't require containing actual code. You could then read in that construct, grab the parts you want, and then pass it to a function or class.
Edit: Added a pass at a cheesy example of a possible JSON spec.
Your JSON:
{
"macros": [
{
"function": "foo_func",
"args": {
"x": "y",
"bar": null
},
"name": "foo",
"iterations": 3
},
{
"function": "bar_func",
"args": {
"x": "y",
"bar": null
},
"name": "bar",
"iterations": 1
}
]
}
Then you parse it with Python's json lib:
import json
# Get JSON data from elsewhere and parse it
macros = json.loads(json_data)
# Do something with the macros
for macro in macros:
run_macro(macro) # For example
And the resulting Python data is almost identical syntactically to JSON aside from some of the keywords like True, False, None (true, false, null in JSON).
{
'macros': [
{
'args':
{
'bar': None,
'x': 'y'
},
'function': 'foo_func',
'iterations': 3,
'name': 'foo'
},
{
'args':
{
'bar': None,
'x': 'y'
},
'function': 'bar_func',
'iterations': 1,
'name': 'bar'
}
]
}